https://rn-wissen.de/wiki/api.php?action=feedcontributions&user=Inka&feedformat=atomRN-Wissen.de - Benutzerbeiträge [de]2024-03-29T11:01:41ZBenutzerbeiträgeMediaWiki 1.25.1https://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=28383Induktive Ladestation für den RP62019-11-29T17:30:10Z<p>Inka: </p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
[http://rn-wissen.de/wiki/index.php/RP6 RP6-V1], [http://rn-wissen.de/wiki/index.php/RP6#RP6_CONTROL_M32_Platine M32], [http://rn-wissen.de/wiki/index.php/RP6_Multi_IO_Projekt MultiIO], LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundaer spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundaer spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
Die verwendeten Bauteile sind stand jetzt (2019-11-30) so nicht mehr beschaffbar...<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule_1.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
--------------------------------------------------------------<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=27733Induktive Ladestation für den RP62016-02-16T19:33:31Z<p>Inka: /* Doppel_ladestation: */</p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
[http://rn-wissen.de/wiki/index.php/RP6 RP6-V1], [http://rn-wissen.de/wiki/index.php/RP6#RP6_CONTROL_M32_Platine M32], [http://rn-wissen.de/wiki/index.php/RP6_Multi_IO_Projekt MultiIO], LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundaer spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundaer spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule_1.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
--------------------------------------------------------------<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=27732Induktive Ladestation für den RP62016-02-16T19:31:51Z<p>Inka: /* Sekundäre Ladespule: */</p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
[http://rn-wissen.de/wiki/index.php/RP6 RP6-V1], [http://rn-wissen.de/wiki/index.php/RP6#RP6_CONTROL_M32_Platine M32], [http://rn-wissen.de/wiki/index.php/RP6_Multi_IO_Projekt MultiIO], LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundaer spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundaer spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule_1.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_doppelspule_geschützt.JPG|thumb|left|Doppelspule geschützt]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
--------------------------------------------------------------<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:Sekundaer_spule_geklebt.JPG&diff=27731Datei:Sekundaer spule geklebt.JPG2016-02-16T19:30:59Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:Sekundaer_spule.JPG&diff=27730Datei:Sekundaer spule.JPG2016-02-16T19:28:59Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:Geh%C3%A4use_ladeschale_gebohrt-2.JPG&diff=27729Datei:Gehäuse ladeschale gebohrt-2.JPG2016-02-16T19:16:56Z<p>Inka: Inka lud eine neue Version von Datei:Gehäuse ladeschale gebohrt-2.JPG hoch</p>
<hr />
<div>Spulengehäuse mit Öffnung</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:Geh%C3%A4use_ladeschale_gebohrt-1.JPG&diff=27728Datei:Gehäuse ladeschale gebohrt-1.JPG2016-02-16T19:14:39Z<p>Inka: Inka lud eine neue Version von Datei:Gehäuse ladeschale gebohrt-1.JPG hoch</p>
<hr />
<div>Spulengehäuse gebohrt</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=26038Induktive Ladestation für den RP62015-01-18T10:59:32Z<p>Inka: /* Doppel_ladestation: */</p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
RP6 V1, m32, multi-IO, LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundär spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundär spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule_1.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_doppelspule_geschützt.JPG|thumb|left|Doppelspule geschützt]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
--------------------------------------------------------------<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_doppelspule_gesch%C3%BCtzt.JPG&diff=26037Datei:2015 01 18 doppelspule geschützt.JPG2015-01-18T10:56:40Z<p>Inka: Inka lud eine neue Version von „Datei:2015 01 18 doppelspule geschützt.JPG“ hoch</p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_doppelspule_1.JPG&diff=26036Datei:2015 01 18 doppelspule 1.JPG2015-01-18T10:56:16Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=26035Induktive Ladestation für den RP62015-01-18T10:46:50Z<p>Inka: /* Doppel_ladestation: */</p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
RP6 V1, m32, multi-IO, LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundär spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundär spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
--------------------------------------------------------------<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Induktive_Ladestation_f%C3%BCr_den_RP6&diff=26034Induktive Ladestation für den RP62015-01-18T10:44:35Z<p>Inka: </p>
<hr />
<div>[[Bild: Ladestation neben RP6-1.JPG|right|Ladestation neben RP6]]<br />
=== Induktive Ladestation für RP6 ===<br />
<br />
====Verwendetes System:====<br />
<br />
RP6 V1, m32, multi-IO, LCD anzeige 4x20<br />
<br />
====Beweggründe, Ziele:====<br />
<br />
Als Beweggrund war für mich der Traum ausschlaggebend einen wirklich autonomen Roboter zu haben. Immer wenn der RP6 am Ladegerät oder einem anderen Kabel hing, hatte ich das Gefühl ihn an einem Pflock angebunden zu haben. Oder – etwas übertrieben - die Vorstellung, die NASA hätte die Kommunikation mit den Mondlandefähren per Kabel realisiert. Ein Albtraum...<br />
<br />
Alles begann schon beim [[Asuro]], dieser erwies sich für notwendige Um – und Aufbauten als zu klein und für ungeschickte Lötversuche als zu anfällig.<br />
<br />
[http://www.roboternetz.de/community/threads/27587-projekt-asuro-sucht-selbst%C3%A4ndig-seine-ladestation?highlight=ladestation projekt: asuro sucht selbständig seine ladestation]<br />
<br />
Die Ladestation lädt den RP6 seit Februar 2014 zuverlässig im abgeschalteten wie im eingeschalteten Zustand auf 8,2V auf. Diese Anleitung beschreibt den Endzustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier tun: [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=RP6-Ladestation: RP6-Ladestation]<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach. An einigen Punkten war ich nahe dran aufzugeben, aber letztendlich wollte ich die Ladestation haben, egal als wie aussichtslos dieser Plan manch Anderem und manchmal auch mir erschien... <br />
<br />
Schon vorab möchte ich mich bei Mitgliedern des RP6-Forums für überragende Hilfe bedanken, ohne diese wäre es wohl bei dem Traum geblieben...<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===Ladestation:===<br />
<br />
<br />
Die Ladestation besteht aus folgenden Baugruppen:<br />
<br />
<br />
<br />
=====Primäre Ladespule mit ihrer Lade-elektronik:=====<br />
<br />
Hier sieht man das Kaufteil , also praktisch das Ausgangsprodukt für den Bau einer induktiven Ladeststion, sowie die primäre Spule mit der Ladeelektronik und dem Anschluss-Stecker ( Alle drei Teile aus dem ursprünglichen Gehäuse entnommen, Stecker wird nicht verwendet )<br />
{| {{Blauetabelle}}<br />
| [[Bild: IQ ladeschale-1.JPG|thumb|left|IQ Ladeschale Kaufteil]] || [[Bild: IQ ladeschale mit ladeelektronik.JPG|thumb|left|IQ Ladespule mit Lade-elektronik und Stecker]]<br />
|}<br />
<br />
Weiter unten sieht man die Bearbeitung des neuen Gehäuses für die primäre Ladespule und die Ladeelektronik.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gehäuse ladeschale gebohrt-1.JPG|thumb|left|Spulengehäuse gebohrt]] || [[Bild: Gehäuse ladeschale gebohrt-2.JPG|thumb|left|Spulengehäuse mit Öffnung]]<br />
|}<br />
Das Gehäuse wurde aus einer TOOM 220V Aufputz-Steckdose mit einer Kantenlänge von 64mm erstellt, das passt zusammen mit dem Gestell aus 4-kantprofilen mit einer Kantenlänge von 10mm sehr gut ( auch zum zentrieren der beiden Spulen zueinander ) zwischen die Felgen der Räder der Antriebsketten (Lichte Weite 84,5mm).<br />
Das Gehäuse wurde auf 9mm gekürzt (Endmaß 8mm, 0,5- 1mm als Aufmaß für Planschleifen der späteren Auflagefläche drauf lassen, die Lichte Weite zwischen der auf der Bodenwanne aufgeklebten sekundären Ladespule und der Auflagefläche beträgt 10mm).<br />
Der vertikale Abstand zwischen den beiden Spulen sollte so gering wie möglich sein, maximal 0,5mm, je größer er wird, um so schlechter spricht die sekundäre Spule an, ab einer bestimmten Entfernung überhaupt nicht mehr.<br />
Der innere „Kragen“ der Steckdose wurde ausgebohrt und mit einer Halbrundfeile auf Außenmaß der Spule erweitert. An der Stelle, wo die geflochtene Litze um die Feritplatte gebogen wird muss das neue Gehäuse noch etwas ausgearbeitet werden (das geht ganz gut mit dem Lötkolben).<br />
<br />
<br />
<br />
<br />
Hier die fertige primäre Ladespule:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Ladespule kpl.JPG|thumb|left|Primäre Ladespule kpl.]] || [[Bild: Ladespule von unten.JPG|thumb|left|Primäre Ladespule von unten]]<br />
|}<br />
Die Spule selbst wurde mit einem 1mm dickem doppelseitigem Klebeband an den Ecken der Feritplatte ( Vorsicht – brüchig!) mit dem Gehäuse verklebt. Später habe ich festgestellt, dass die Spule nach einer gewissen Zeit (über Nacht) warm wird und dadurch das Klebeband an Haftung verliert, die Spule kippt dann teilweise nach unten und der Ladevorgang wird unterbrochen, da sollte man also noch etwas tun – richtig mit UHU-Plus Schnellfest (oder ähnlich) kleben.<br />
<br />
Die Ladeelektronik wurde auf der Rückseite der Feritplatte mit dem gleichen Klebeband an der Lötseite mit der Feritplatte verklebt, das Kabel an der Elektronik angeschlossen. Plus und Minus an der Elektronik sind nicht gekennzeichnet, Vorsicht beim Anschluss des (evtl. gekürzten) USB-Kabels, Lage von rot und schwarz merken! Das Kabel habe ich mit UHU Plus angeklebt (Zugentlastung). Am anderen Ende des USB-Kabels ist ein USB-Steckernetzteil mit einem Ausgangsstrom von 1A. PC-USB Anschluss hat von der Leistung her nicht gereicht.<br />
<br />
Hier noch die Belegung des USB Steckers:<br />
{| {{Blauetabelle}}<br />
| [[Bild: USB Stecker pinbelegung.png|thumb|left|Pinbelegung USB-Stecker]]<br />
|}<br />
<br />
=====Sekundäre Ladespule:=====<br />
Die Spule ist an der Bodenwanne im vorderen Bereich mit dünnem Doppelseitigem Klebeband mittig zur Wanne des RP6 aufgeklebt (möglichst etwas weniger schief als hier), die Kabel sind mit einem tropfen Kleber fixiert<br />
{| {{Blauetabelle}}<br />
| [[Bild: Sekundär spule.JPG|thumb|left|Sekundärspule mit Anschlussdrähten]] || [[Bild: Sekundär spule geklebt.JPG|thumb|left|Sekundärspule an der Bodenwanne geklebt]]<br />
|}<br />
<br />
=====Step-Up-Converter:=====<br />
<br />
Der Wandler ist im Heck-seitigem Bereich der Bodenwanne(unterhalb des EXT-Anschlusses) mit einem dickeren doppelseitigem Klebeband aufgeklebt (Lötspitzen auf der Lötseite des Converters verhindern andere Befestigung, Bohrungen zum Anschrauben hat der Wandler nicht).<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step-up-converter.jpg|thumb|left|Step-Up-Converter 4V auf 30V]] || [[Bild: Step up converter montiert-1.JPG|thumb|left|Step-Up-Converter auf der Bodenwanne montiert]]<br />
|}<br />
<br />
Am Step-Up_converter muss vorher mit dem Potentiometer (am besten auf dem Steckbrett, dazu habe ich die Lötpads mit Lötstiften versehen) die Ausgangsspannung auf 9V eingestellt werden. Diese Stifte wurden später so angepasst bzw. gegen neue getauscht, dass dort die Gegenstecker der Verdrahtung drauf gesteckt werden konnten.<br />
<br />
Bei den ersten Tests der Schaltung lief der Wandler nicht an. Die Versuche mit einem Graetz Gleichrichter brachten keine Besserung, ein – mehr oder weniger aus Verzweiflung - gemachter Versuch mit einer 1A Diode am Eingang des Wandlers brachte die Lösung. Der Wandler läuft nach dem einschalten der Ladestation nach ca. 5-10 Sekunden zuverlässig an. Hier der Wandler auf dem Steckbrett, rechts mit der SMD-Diode auf IN+ :<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Step up converter auf dem steckbrett.JPG|thumb|left|Step-Up-Converter auf dem Steckbrett]] || [[Bild: SMD-diode auf IN- steckbrett.JPG|thumb|left|SMD Diode auf Anschluss IN+ gelötet]]<br />
|}<br />
<br />
Am Konverter wurden später, während des Ladens, folgende Werte gemessen:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Messungen strom spannung beim laden.png|thumb|left|Spannungs- und Stromwerte während des Ladens]]<br />
|}<br />
<br />
Durch Selbstentladung während der nächsten 12 Stunden verringerte sich die hier gemessene Spannung am Voltmeter auf 8V und der Vbat Wert auf 8,1V.<br />
<br />
=====Gestell:=====<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Gestell von oben.JPG|thumb|left|Ladestation von oben]] || [[Bild: Gestell von unten.JPG|thumb|left|Ladestation von unten]] || [[Bild: Lichtleitfaser an der elektronik.JPG|thumb|left|Lichtleitfaser an der LED der Lade-elektronik]] || [[Bild: Blaue LED als Ladekontrolle.JPG|thumb|left|blaue LED leuchtet, wenn die Ladestation an ist, beim Laden blinkt sie]]<br />
|}<br />
<br />
[https://www.youtube.com/watch?v=XqvEPsfwQtE?highlight= LED beim Laden]<br />
<br />
Gefertigt aus 4kant Profilen 10x1, die erhöhte Stirnseite dient als Anschlag für die Positionierung der Spulen zueinander, die beiden seitlichen Profile liegen beim Laden zwischen den Ketten, damit werden die Spulen in seitlicher Richtung zentriert.<br />
Steht der RP6 auf diesem Gestell, haben die Ketten einen Abstand zu Tisch/Bodenplatte und der RP6 kann zu Testzwecken betrieben und gleichzeitig geladen werden. Die Ladestation kann aber auch ohne das Gestell betrieben werden, der RP6 steht dann mit den Ketten auf der Auflage / dem Boden.<br />
<br />
=====Verdrahtung:=====<br />
<br />
Die Verdrahtung verläuft im wesentlichen über die Base-platine und über die Heck- und Front-seitige Flächen der Bodenwanne. Die Verdrahtung des Step-Up-Converters ist auch aus dem vorhergehendem Foto der Einbausituation des Wandlers ersichtlich. Alle Anschlüsse sind mit Steckern versehen um eine Demontage der Base-Leiterplatte möglich zu machen.<br />
Will man die Einstellung der (alten) Encoder verändern, muss man vorsichtig die Kleberstreifen vom Wandler entfernen und ihn abnehmen. Bei den neuen Encodern ist ein Demontieren der Gehäusehälften nicht erforderlich, der Step-Up-Converter kann also aufgeklebt bleiben. Daneben noch einmal ein Bild der kompletten Verdrahtung am RP6 im Zusammenhang mit der Ladestation.<br />
{| {{Blauetabelle}}<br />
| [[Bild: Kabelverlauf.JPG|thumb|left|Kabelverlauf auf der Baseplate]] || [[Bild: Verdrahtung auf RP6-2.JPG|thumb|left|Verdrahtung der Lade-elektronik am RP6]]<br />
|}<br />
<br />
=====Bauteilebedarf:=====<br />
<br />
<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Name''' || '''Webseite''' || '''Distributor'''<br />
|-<br />
| Ladeschale und Empfängerspule || Qi Wireless Kabellose Ladegerät Pad Receiver für Samsung Galaxy S3 || ebay || http://www.ebay.de/itm/Qi-Wireless-Kabellose-Ladegerat-Pad-Receiver-fur-Samsung-Galaxy-S3-S4-Note-2-ED-/171176612540?pt=DE_M%C3%B6bel_Wohnen_Sonstige&var=&hash=item27daebd2bc<br />
|-<br />
| Step-up-wandler || LM2577S DC-DC-Wandler Step-up Power Supply Modul || ebay || http://www.ebay.de/itm/290943258842?ssPageName=STRK:MESINDXX:IT&_trksid=p3984.m1436.l2649<br />
|-<br />
| SMD Diode || 1A / 50V Diotec SM4001 || Conrad || http://www.conrad.de/ce/de/product/160725/?insert=62&insertNoDeeplink&productname=SMD-Diode-1-A-Diotec-SM4001-Gehaeuseart-MELF-IF-1-A-Sperrspannung-UR-50-V<br />
|-<br />
| || || ||<br />
|}<br />
<br />
<br />
=====Doppel_ladestation:=====<br />
Die Ladestation wurde erweitert:<br />
<br />
- Durch Verwendung von zwei Ladespulen kann der RP6 in zwei Positionen geladen werden, so sind Teile / Sensoren auch während des Ladens alle zugänglich (der RP6 muss nur einmal gedreht werden, der Ladevorgang wird sofort nach dem Drehen fortgesetzt).<br />
<br />
- Die Ladespulen können getrennt oder gemeinsam betrieben werden. Beim Laden mit beiden Ladespulen gleichzeitig liegt der Ladestrom bei 350mA. <br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2015_01_18_doppel_lader.jpg|thumb|left|Doppel_ladestation von oben]] || [[Bild: 2015_01_18_doppelspule.JPG|thumb|left|Doppelspule am Unterboden]] || [[Bild: 2015_01_18_laden_pos_1.JPG|thumb|left|Ladeposition_1]] || [[Bild: 2015_01_18_laden_pos_2.JPG|thumb|left|Ladeposition_2]]<br />
|}<br />
<br />
<br />
<br />
----------------------------------------------------------------------<br />
Die Ladestation ist sicher nicht perfekt, mein Traum hat sich aber voll erfüllt.<br />
<br />
<br />
<br />
<br />
<br />
Und nun - viel Spaß und Erfolg beim Nachbauen...<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 09:50, 12. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_laden_pos_2.JPG&diff=26033Datei:2015 01 18 laden pos 2.JPG2015-01-18T10:26:14Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_laden_pos_1.JPG&diff=26032Datei:2015 01 18 laden pos 1.JPG2015-01-18T10:25:41Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_doppelspule_gesch%C3%BCtzt.JPG&diff=26031Datei:2015 01 18 doppelspule geschützt.JPG2015-01-18T10:25:14Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_doppelspule.JPG&diff=26030Datei:2015 01 18 doppelspule.JPG2015-01-18T10:24:47Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2015_01_18_doppel_lader.jpg&diff=26029Datei:2015 01 18 doppel lader.jpg2015-01-18T10:24:21Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25854IR-bake für den RP62015-01-03T10:54:23Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake, zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_11_12_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake 1.1, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_11_12_ir_bake_1_1.jpg&diff=25853Datei:2014 11 12 ir bake 1 1.jpg2015-01-03T10:52:02Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25852IR-bake für den RP62015-01-03T10:48:37Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake, zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake 1.1, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25851IR-bake für den RP62015-01-03T10:47:46Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake, zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
<br />
<br />
Hier eine überarbeitete Version des Stromlaufplanes der kompletten IR-Bake 1.1, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25850IR-bake für den RP62015-01-03T10:46:39Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake, zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht)<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
<br />
<br />
Hier eine überarbeitete Version des Stromlaufplanes der kompletten IR-Bake 1.1, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht):<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25849IR-bake für den RP62015-01-03T10:44:11Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten IR-Bake, zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1 (Aufgebaut und getestet wurde diese Version der Schaltung nicht)<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25848IR-bake für den RP62015-01-03T10:43:11Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1) (Aufgebaut und getestet wurde diese Version der Schaltung nicht)<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25847IR-bake für den RP62015-01-03T10:42:25Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
Aufgebaut und getestet wurde diese Version der Schaltung nicht...<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25846IR-bake für den RP62015-01-03T10:41:42Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
Aufgebaut und getestet wurde diese Version der Schaltung nicht...<br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25845IR-bake für den RP62015-01-03T10:40:22Z<p>Inka: /* Stromlaufplan des Blinkers: */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine korrigierte Schaltung siehe Stromlaufplan Bake komplett...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
Getestet bzw. verdrahtet habe ich diese Version der Schaltung nicht...<br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25844IR-bake für den RP62015-01-03T09:39:55Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
Getestet bzw. verdrahtet habe ich diese Version der Schaltung nicht...<br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
hier eine überarbeitete Version des Stromlaufplanes, diese wurde allerdings nicht getestet bzw. verdrahtet...<br />
<br />
<center>[[Bild: 2014_12_15_ir_bake_1_1_kpl.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl. (korrigiert)]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25843IR-bake für den RP62015-01-03T09:34:05Z<p>Inka: </p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
Getestet bzw. verdrahtet habe ich diese Version der Schaltung nicht...<br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014_10_08_ir_bake_1_1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_12_15_ir_bake_1_1_kpl.jpg&diff=25842Datei:2014 12 15 ir bake 1 1 kpl.jpg2015-01-03T09:30:37Z<p>Inka: Inka lud eine neue Version von „Datei:2014 12 15 ir bake 1 1 kpl.jpg“ hoch</p>
<hr />
<div>bake_1_1, korrigiert</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_10_08_ir_bake_1_1.jpg&diff=25841Datei:2014 10 08 ir bake 1 1.jpg2015-01-03T09:29:36Z<p>Inka: bake_1_1_kpl</p>
<hr />
<div>bake_1_1_kpl</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25840IR-bake für den RP62015-01-03T09:15:15Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
<br />
Hier der korrigierter Stromlaufplan der kompletten Bake (zusammengesteckt und mit zwei NE555 versehen, Korrektur bei LED1):<br />
<center>[[Bild: 2014_12_15_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan (korrigiert) IR-bake kpl.]] </center><br />
Getestet bzw. verdrahtet habe ich diese Version der Schaltung nicht...<br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_12_15_ir_bake_1_1_kpl.jpg&diff=25839Datei:2014 12 15 ir bake 1 1 kpl.jpg2015-01-03T09:08:30Z<p>Inka: bake_1_1, korrigiert</p>
<hr />
<div>bake_1_1, korrigiert</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_12_15_ir_bake_1_kpl.jpg&diff=25838Datei:2014 12 15 ir bake 1 kpl.jpg2015-01-03T09:06:26Z<p>Inka: bake_1_0_korrigiert</p>
<hr />
<div>bake_1_0_korrigiert</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25413IR-bake für den RP62014-11-13T15:32:37Z<p>Inka: /* Stromlaufplan des Blinkers: */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
------------------------------------------------------------------------<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
------------------------------------------------------------------------<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25412IR-bake für den RP62014-11-13T07:49:17Z<p>Inka: /* Bauteilebedarf */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || 20mA || LED1 || 1<br />
|-<br />
| LED 3mm grün || 20mA || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25411IR-bake für den RP62014-11-13T07:45:55Z<p>Inka: /* Stromlaufplan kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: 2014_11_12_ir_bake_1_kpl.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot/20mA || || LED1 || 1<br />
|-<br />
| LED 3mm grün/20mA || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_11_12_ir_bake_1_kpl.jpg&diff=25410Datei:2014 11 12 ir bake 1 kpl.jpg2014-11-13T07:44:18Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25409IR-bake für den RP62014-11-13T07:41:07Z<p>Inka: /* Stromlaufplan des Blinkers: */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
im zugehörigen Thread im Forum[http://www.roboternetz.de/community/threads/63467-IR-bake/page14?p=599832&viewfull=1#post599832] wird - auch wenn die Schaltung in der Praxis funktioniert - zurecht darauf hingewiesen, dass es möglich ist, dass die Funktion z.B. durch Bauteiltoleranzen beeinträchtigt wird:<br />
<br />
Der Teil mit dem Blinker ist verdächtig: die Kopplung zum IR Sender sollte mit der LED nicht mehr Funktionieren. Also besser die rote LED direkt an den Ausgang des NE555, und den Transistor nur zur Kopplung mit der IR Stufe. Bei der LED sollten es auch mehr als 47 Ohm Vorwiederstand sein. Alternativ könnte man die Kopplung über den Reset Eingang des NE555 machen - das geht auch ohne den Transistor.<br />
<br />
Eine bearbeitete Schaltung wird demnächst hier veröffentlicht...<br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot/20mA || || LED1 || 1<br />
|-<br />
| LED 3mm grün/20mA || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25408IR-bake für den RP62014-11-13T07:24:13Z<p>Inka: </p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot/20mA || || LED1 || 1<br />
|-<br />
| LED 3mm grün/20mA || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25391IR-bake für den RP62014-11-01T10:27:05Z<p>Inka: /* IR-bake kpl. */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014_11_01_breadboard_bake_1_1.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=Datei:2014_11_01_breadboard_bake_1_1.JPG&diff=25390Datei:2014 11 01 breadboard bake 1 1.JPG2014-11-01T10:26:17Z<p>Inka: </p>
<hr />
<div></div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25389IR-bake für den RP62014-10-31T14:47:17Z<p>Inka: /* IR-bake 1.1 für RP6 */</p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der Jumper ersichtlich: https://www.youtube.com/watch?v=qXHBNYFRYBc<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25388IR-bake für den RP62014-10-31T14:23:35Z<p>Inka: </p>
<hr />
<div>[[Bild: 2014 10 31 baken vergleich.JPG|500px|right|IR-bake 1.0 und 1.1]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25387IR-bake für den RP62014-10-31T14:20:52Z<p>Inka: /* IR-bake kpl. */</p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
====IR-bake kpl.====<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25386IR-bake für den RP62014-10-31T14:19:20Z<p>Inka: /* IR-bake kpl. */</p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
== IR-bake kpl. ==<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25385IR-bake für den RP62014-10-31T14:18:19Z<p>Inka: /* IR-bake kpl. */</p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
==IR-bake kpl.==<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25384IR-bake für den RP62014-10-31T14:17:23Z<p>Inka: /* Navigation mit Hilfe von zwei baken (1.0 und 1.1) */</p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
===IR-bake kpl.===<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
===Navigation mit Hilfe von zwei baken (1.0 und 1.1)===<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25383IR-bake für den RP62014-10-31T14:16:18Z<p>Inka: /* IR-bake 1.1 für RP6 */</p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
===IR-bake 1.1 für RP6===<br />
<br />
<br />
===IR-bake kpl.===<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
<br />
aus diesem Video ist die Funktion der JUmper ersichtlich...<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
== Navigation mit Hilfe von zwei baken (1.0 und 1.1) ==<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inkahttps://rn-wissen.de/wiki/index.php?title=IR-bake_f%C3%BCr_den_RP6&diff=25382IR-bake für den RP62014-10-31T14:14:47Z<p>Inka: </p>
<hr />
<div>[[Bild: IR-bake bauteilseite mit reflektoren.JPG|500px|right|IR-bake kpl. mit Reflektoren]]<br />
===IR-bake 1.0 für RP6===<br />
===Verwendetes System:===<br />
<br />
RP6 V1, m32, multi-IO, LCD Display 4x20<br />
<br />
===Beweggründe, Ziele:===<br />
<br />
Im laufe des Projektes für die [http://www.roboternetz.de/community/threads/62934-RP6-Projekt-induktive-Ladestation?highlight=induktive: induktive Ladestation] ergab sich die Frage, ob diese Ladestation auch zuverlässig mit Hilfe einer IR- bake gefunden werden kann.<br />
<br />
Die Anleitung beschreibt den IST-Zustand des Projektes, ohne groß auf die Sackgassen und Irrwege aus der Entwicklungszeit einzugehen. Wenn aber irgendwo Bauteile oder Programmzeilen auftauchen, die ohne Hinweise auf diesen Prozess unverständlich wären, versuche ich detaillierter auf das Thema einzugehen. Wer den ganzen Weg nachverfolgen will (eigentlich empfehlenswert) kann es hier ( [http://www.roboternetz.de/community/threads/63467-IR-bake/page1?highlight=IR-bake: IR-bake] ) tun. Am Ende finden sich auch ein Paar Ideen was und wie man weitermachen könnte...<br />
<br />
Mir ging es hier mehr oder weniger darum, nachzuweisen, dass es mit relativ geringem Aufwand möglich ist, mit Hilfe dieser IR-bake die Ladestation (oder was auch immer) über eine Entfernung von 5-6 Metern zu finden.<br />
<br />
Last but not least – es soll eine Anleitung vom Anfänger für Anfänger sein, ich hoffe ich kann diesem Anspruch gerecht werden. Das Wort „einfach“ habe ich versucht zu vermeiden, für mich war es auch nicht einfach, trotz Hilfe aus dem Forum...<br />
<br />
<br />
Eine reine Vorsichtsmaßnahme – jeglicher Nachbau geschieht auf eigenes Risiko...<br />
<br />
<br />
<br />
hier sind die Ursprünge der Schaltungen:<br />
<br />
IR-bake (Stromlaufplan und Beschreibung) http://www.robotroom.com/Infrared555.html<br />
<br />
Blinker (astabile Kippstufe mit einstellbaren Zeiten) http://www.domnick-elektronik.de/elek555.htm<br />
<br />
<br />
<br />
<br />
<br />
Und nun geht es zu der Anleitung...<br />
<br />
===IR-Empfänger===<br />
<br />
Als Empfänger soll der IR-Empfänger des RP6 in unveränderter Form Verwendung finden<br />
<br />
===IR-Sender===<br />
<br />
Angefangen habe ich mit dem Aufbau des IR-Senders, experimentiert habe ich später – um die Leistung zu steigern - mit den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2. Die Werte im Stromlaufplan sind der letzte Stand.<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR sender auf steckbrett.jpg|thumb|left|IR-Sender auf Steckbrett]] || [[Bild: IR-bake loetseite.JPG|thumb|left|IR-Sender Lötseite]] || [[Bild: IR-bake bauteilseite.JPG|thumb|left|IR-Sender Bauteilseite]]<br />
|}<br />
<br />
Das Steckbrettmodell habe ich recht früh gegen eine gelötete Version getauscht, die Wackelkontakte beim Steckbrett haben mich gestört...<br />
<br />
<br />
Der grundsätzlichen Kontrolle ob die Schaltung läuft dient die grüne LED (rote am Steckbrett), ob die IR-LEDs leuchten kann man letztendlich nur mit einer digitalen Kamera sehen.<br />
<br />
<br />
<br />
<br />
Die verwendeten IR-LEDs sind die gleichen, wie sie auch im RP6 verbaut sind (LD 271 L), 5 deshalb, weil man damit recht gut einen Halbkreis abdecken kann.<br />
<br />
Die beiden USB-Stecker sind von der Funktion her nicht erforderlich, bei verschiedenen Testaufbauten haben sie sich aber auch als mechanische Halterungen für die bake und für den Test für den Abstand der IR-Strahlungsebene vom Boden bestens bewährt.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: IR bake sender unten.JPG|thumb|left|IR-Sender im Halter, IR-Dioden unten]] || [[Bild: IR bake sender oben.JPG|thumb|left|IR-Sender im Halter, IR-Dioden oben]] || [[Bild: IR-bake mit netzteil kpl haengend.JPG|thumb|left|IR-bake kpl. am Kabel hängend]] || [[Bild: IR-bake mit netzteil kpl stehend.JPG|thumb|left|IR-bake kpl. im Halter stehend]]<br />
|}<br />
<br />
Im Verlauf der Tests habe ich verschiedene Bauteile nachträglich „gesockelt“, wenn jemand verschiedene Bauteilkombinationen testen will und sich spätere mühsame Lötarbeit ersparen will – der Steckbrettaufbau (zumindest meiner) eignet sich für solche Tests nicht - sollte es von vorne herein vorsehen (Widerstände in der Basisleitung des Q2, der Q2 selbst, Vorwiderstände der LEDs, der IC2, evtl. die IR-LEDs – wenn man leistungsfähigere einsetzen möchte.<br />
<br />
====Stromlaufplan des Senders:====<br />
<br />
<center>[[Bild: IR bake sender blatt 2.jpg|600px|none|Stromlaufplan IR-bake Sender]] </center><br />
<br />
<br />
====Softwareänderungen RP6 base====<br />
Um den IR-Sender mit dem IR- Empfangssystem des RP6 testen bzw. später auch betreiben zu können, musste die Software für die RP6-Basis geändert werden:<br />
<br />
in der "RP6Base_I2CSlave.c" wurde das Register 30 eingeführt:<br />
<br />
<pre><br />
(die Zeilennummern sind „meine“ Zeilennummern!)<br />
-----------------------------------------------------------------------------<br />
Zeile Inhalt Vorgang<br />
-----------------------------------------------------------------------------<br />
85 uint8_t pb2_value; //hinzu<br />
231 #define I2C_REG_PB2_VALUE 30 //hinzu<br />
272 2CTWI_readRegisters[I2C_REG_PB2_VALUE] = (uint8_t)(pb2_value); //hinzu<br />
483 pb2_value = (PINB & (1<<PINB2)); //hinzu<br />
------------------------------------------------------------------------------<br />
</pre><br />
<br />
--------------------------------<br />
Wenn kein IR-Signal anliegt, hat der Empfänger High-Pegel, dann ist PINB2 (also Bit 2) gesetzt, binär = 0b00000100 (dezimal 4), der Inhalt von Register 30 =4. Wenn ein IR-Signal anliegt, ist das Bit nicht gesetzt, binär = 0b00000000 (dezimal 0), also Register 30 = 0.<br />
---------------------------------<br />
Die Funktion „readAllRegisters“ (verwendet in einer der Demos) wurde – um auch das Register 30 lesen zu können - geändert in:<br />
<br />
<pre><br />
/***********************readAllRegisters********************/<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
/*******************************************************/<br />
</pre><br />
<br />
Es wurde später noch eine neue Funktion eingeführt in der nur das Register 30 gelesen und ausgegeben wird, dazu aber später.<br />
<br />
====Testaufbau IR- Sender====<br />
<br />
Die IR-bake wurde zunächst in diesem Testaufbau getestet:<br />
[[Bild: Testplatz IR-Bake.JPG|thumb|left|Testaufbau IR-Sender]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Entfernung Sender - Empfänger ca. 30cm, wurde auf dem IR-Sender – zunächst ohne die Blinkerplatine - am Poti R6 die Sendefrequenz von 36kHz eingestellt:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Aufnahme 7DIV 5per.JPG|thumb|left|36kHz des IR-Senders auf dem Oszilloskop]] || [[Bild: Einstellungen HAMEG.JPG|thumb|left|Einstellungen am Oszilloskop]] || [[Bild: Anschluss hameg.JPG|thumb|left|Anschluss des IR-Senders]] || [[Bild: Frequenz berechnung IR bake.png|thumb|left|Tabelle Frequenzberechnung IR-Sender]]<br />
|}<br />
<br />
das Foto links entspricht ca. 36kHz (7DIV und 5.95 Perioden), daneben die dazu notwendigen Einstellungen am Oszilloskop, rechts der Anschluss des OSZI an der Senderplatine (Masse und Kollektor von Q2). Ganz rechts noch eine Tabelle, mit der ich die Frequenz berechnet habe:<br />
<br />
<br />
Ich kann mir vorstellen, dass man bei laufender Software am RP6 die Einstellung der 36kHz (mit der „readAllRegister“ Funktion und sendendem IR-Sender) anhand des Empfangs oder eben keinen Empfangs am RP6 auch ohne Oszilloskops hin bekommt.<br />
<br />
<br />
Die Versuche mit der IR-bake allein haben gezeigt, dass die Änderungen an den Vorwiderständen der IR-LEDs und den Basiswiderständen des Q2 bei den im Stromlaufplan angegebenen Werten Schluss ist, der Q2 wurde dann zu heiß ohne dass sich die Entfernung zwischen Sender und Empfänger nennenswert erhöhen ließ. <br />
<br />
Um die Leistung des Senders zu erhöhen und die IR-LEDs vor Überhitzung und Ausfall zu schützen kommt jetzt der Blinker ins Spiel.<br />
<br />
===Blinkerschaltung:===<br />
<br />
Da die Senderplatine bereits fertig war, habe ich mich dazu entschlossen eine neue Schaltung quasi „huckepack“ auf die Lötseite der Senderplatine zu setzen, mit vier Stift- und Buchsenkontakten steckbar verbunden. Drei von ihnen verbindend, der vierte nur als mechanische Stabilisierung der oberen Leiterplatte. Die Kontakte haben „oben und unten“unterschiedliche Abstände, um falsches Stecken zu vermeiden. Durch die steckbare Blinkerplatine konnte ich die Blinkfrequenz unabhängig von der Senderplatine einstellen (Potis R1 und R2). Es ging aber auch mit Einzellitzen als Steckverbindungen zwischen der Sender- und Blinkerplatine (siehe Bilder im Abschnitt „Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“).<br />
<br />
Vorsicht – die Senderschaltung funktioniert auch ohne die Blinkerplatine im „Dauerfeuer“, der Q2 auf der Senderplatine wird allerdings ziemlich heiß!<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Blinker bauteilseite-1.JPG |thumb|left|Blinker Bauteilseite]] || [[Bild: Blinker loetseite-1.JPG|thumb|left|Blinker Lötseite]] || [[Bild: Sender und blinker steckposition.jpg|thumb|left|IR-Sender und Blinker in Steckposition]]<br />
|}<br />
<br />
====Stromlaufplan des Blinkers:====<br />
<center>[[Bild: IR bake blinker blatt 1.jpg|600px|none|Stromlaufplan IR-bake Blinker]] </center><br />
<br />
====Testaufbau des Blinkers====<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 sender blinker mit litzen verbunden.JPG |thumb|left|Messaufbau mit Litzen zwischen IR-Sender und Blinker]] || [[Bild: Testaufbau blinker.JPG |thumb|left|Testaufbau Blinker]] || [[Bild: Testplatz IR-Bake.JPG |thumb|left|Testaufbau IR-bake]]<br />
|}<br />
<br />
<br />
Mit den Potentiometern R1 und R2 kann die Dauer der Ein- und Aus- Phasen des Blinkers eingestellt werden, siehe der Abschnitt „ Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake“.<br />
<br />
===IR-bake kpl.===<br />
Man kann es sich beim Nachbau überlegen, ob man die beiden Leiterplatten zu einer zusammenfasst und eventuell statt zwei NE555 einen NE556 verwendet. Sicher möglich, aber das muss jeder für sich entscheiden. Wahrscheinlich vereinfacht es die Schaltung, evtl. aber wird die Einstellung der beiden Funktionen (Sender und und Blinker) erschwert.<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
Der Stromlaufplan beider (hier zusammengesteckt und mit zwei NE555 versehen) Platinen zusammen:<br />
<center>[[Bild: Ir bake kpl hochkant.jpg|600px|none|Stromlaufplan IR-bake kpl.]] </center><br />
<br />
====Testaufbau IR-bake kpl.====<br />
<br />
2014_05_04_case_1_signaltest Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
<youtube>fpGOCCoK33o</youtube><br />
<br />
2014_05_04_case_1_signaltest 5m Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m. Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
<youtube>JfxO0yqqR7g</youtube><br />
<br />
2014_05_14_case_1_signaltest_6m Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
<youtube>nV88c8zB5t4</youtube><br />
<br />
2014_05_04_case_2_signaltest Testaufbau für case2, Entfernung 85cm <br />
<br />
<youtube>nsxvsr4du6Y</youtube><br />
<br />
===Anpassung der Sende und Empfangsfrequenz zwischen RP6 und der bake===<br />
<br />
mit dieser Software wurden die Frequenzen aneinader angepasst:<br />
<br />
<pre><br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "standard.h"<br />
//#include "navi_1.h"<br />
<br />
<br />
#define I2C_RP6_BASE_ADR 10<br />
<br />
/******************variablen***************************/<br />
<br />
uint8_t transmit_buffer[10];<br />
uint8_t RP6data[32];<br />
<br />
uint8_t i, j;<br />
uint8_t temp;<br />
<br />
<br />
uint8_t feld_IR[200];<br />
uint8_t IR_wert[1];<br />
<br />
/****************variablen folge linie********************/<br />
unsigned char speed;<br />
int16_t speedLeft,speedRight;<br />
//uint8_t speedLeft,speedRight;<br />
//unsigned int lineData[2];<br />
uint16_t lineData[2];<br />
int x, xalt, don, doff, kp, kd, ki, yp, yd, yi, drest, y, y2, isum;<br />
<br />
/**************follow line***************************/<br />
void FollowLine (void)<br />
{<br />
unsigned char leftDir = FWD, rightDir = FWD;<br />
<br />
// LineData(lineData); // Messung mit LED ON<br />
lineData[0] = getLFS(CH_LFS_L);<br />
lineData[1] = getLFS(CH_LFS_R);<br />
x = (lineData[0] - lineData[1]);<br />
// x = don - doff; // Regelabweichung<br />
// x = don;<br />
isum += x;<br />
if (isum > 16000) isum =16000; //Begrenzung um Überlauf zu vermeiden<br />
if (isum < -16000) isum =-16000;<br />
yi = isum/625 * ki; //I-Anteil berechnen<br />
yd = (x - xalt)*kd; // D-Anteil berechnen und mit<br />
yd += drest; // nicht berücksichtigtem Rest addieren<br />
if (yd > 255) drest = yd - 255; // merke Rest<br />
else if (yd < -255) drest = yd + 255;<br />
else drest = 0;<br />
// if (isum > 15000) BackLED(OFF,ON); // nur zur Diagnostik<br />
// else if (isum < -15000) BackLED(ON,OFF);<br />
// else BackLED(OFF,OFF);<br />
yp = x*kp; // P-Anteil berechnen<br />
y = yp + yi + yd; // Gesamtkorrektur<br />
y2 = y/2; // Aufteilung auf beide Motoren<br />
xalt = x; // x merken<br />
speedLeft = speedRight = speed;<br />
setMotorDir(FWD,FWD);<br />
if ( y > 0) // nach rechts<br />
{<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
speedLeft = speed + y2; // links beschleunigen<br />
if (speedLeft > 255)<br />
{<br />
speedLeft = 155; // falls Begrenzung<br />
y2 = speedLeft - speed; // dann Rest rechts berücksichtigen<br />
}<br />
y = y - y2;<br />
speedRight = speed - y; // rechts abbremsen<br />
if (speedRight < 0)<br />
{<br />
speedRight = 0;<br />
}<br />
}<br />
if ( y < 0) // nach links<br />
{<br />
setMultiIOLED2(1); //StatusLED(RED);<br />
speedRight = speed - y2; // rechts beschleunigen !!!was speed - y2!!<br />
if (speedRight > 255)<br />
{<br />
speedRight = 155; // falls Begrenzung<br />
y2 = speed - speedRight; // dann Rest links berücksichtigen !!was speed - speedRight!!<br />
}<br />
y = y - y2;<br />
speedLeft = speed + y; // links abbremsen !! was speed + y!!!<br />
if (speedLeft < 0)<br />
{<br />
speedLeft = 0;<br />
}<br />
}<br />
leftDir = rightDir = FWD;<br />
if (speedLeft < 20) mleft_speed = 0; //leftDir = BREAK; // richtig bremsen<br />
if (speedRight < 20) mright_speed = 0; //rightDir = BREAK;<br />
setMotorDir(leftDir,rightDir);<br />
//MotorSpeed(abs(speedLeft),abs(speedRight));<br />
setMotorPower(abs(speedLeft),abs(speedRight));<br />
}<br />
<br />
<br />
/********************hauptprogramm*****************************/<br />
<br />
int main(void)<br />
{<br />
initRP6Control();<br />
multiio_init();<br />
initLCD();<br />
<br />
<br />
<br />
setLEDs(0b1111);<br />
mSleep(500);<br />
setLEDs(0b0000);<br />
<br />
I2CTWI_initMaster(100);<br />
I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); //aktiviert I2C fehlermeldungen<br />
<br />
showScreenLCD(" RP6Control M32", " Ir_bake_test_anl", "", "");<br />
mSleep(2500);<br />
clearLCD();<br />
accuspannung();<br />
mSleep(1500);<br />
<br />
<br />
while(true)<br />
{<br />
<br />
accuzustand();<br />
<br />
<br />
<br />
<br />
/*****************anzeige gedrückter buttons****************/<br />
<br />
clearLCD();<br />
pressedMultiIOButtonNumber = getMultiIOPressedButtonNumber();<br />
<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("1: einzelsignal");<br />
setCursorPosLCD(1, 0);<br />
writeStringLCD("2: feldvariable_10ms");<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD("3: folge_linie");<br />
setCursorPosLCD(3, 0);<br />
writeStringLCD("4: ????");<br />
mSleep(1500);<br />
<br />
uint8_t key = getMultiIOPressedButtonNumber();<br />
<br />
<br />
/********************funktion der buttons*********************/<br />
if(key)<br />
{<br />
switch(key)<br />
{<br />
case 1://<br />
setLEDs(0b0001);<br />
writeString_P("\n\n messung einzelsignal\n");<br />
<br />
initRP6Control();<br />
initLCD();<br />
<br />
// ---------------------------------------<br />
WDT_setRequestHandler(watchDogRequest);<br />
<br />
/*************************************************************/<br />
<br />
while(true)<br />
{<br />
<br />
<br />
<br />
read_Register_30();<br />
setMultiIOLED1(0);<br />
setMultiIOLED2(0);<br />
setMultiIOLED4(0);<br />
setMultiIOLED3(1);<br />
mSleep(100);//600,<br />
if (IR_wert[0] == 0)<br />
{<br />
<br />
setMultiIOLED1(1);<br />
setMultiIOLED3(0);<br />
mSleep(50);//500, 150<br />
<br />
}<br />
<br />
// readAllRegisters();<br />
// print_Register_30();<br />
// mSleep(100);<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 2://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 10ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
for(j = 0; j < 120; j+=10)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
<br />
case 3://<br />
setLEDs(0b0100);<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD("folge_linie");<br />
mSleep(1500);<br />
clearLCD();<br />
lfsbumper_init();<br />
accuzustand();<br />
<br />
<br />
// startStopwatch3();<br />
<br />
speed = 20; //50; //100;<br />
kp = 3;<br />
ki = 10;<br />
kd = 70; // Regler Parameter kd enthält bereits Division durch dt<br />
drest=0;<br />
isum=0;<br />
xalt=0;<br />
// sw=0;<br />
<br />
<br />
while(true)<br />
{<br />
setMotorDir(FWD,FWD);<br />
setMultiIOLEDs(0);<br />
mSleep(100);<br />
setMultiIOLED1(1); //StatusLED(GREEN);<br />
mSleep(100);<br />
<br />
<br />
<br />
speedLeft = speed;<br />
speedRight = speed+25;<br />
<br />
FollowLine();<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0)<br />
{<br />
stop();<br />
break;<br />
}<br />
<br />
// }<br />
}<br />
<br />
break;<br />
<br />
case 4://<br />
setLEDs(0b0010);<br />
writeString_P("\n\n messung feldvariable step 5ms\n\n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
<br />
while(true)<br />
{<br />
<br />
<br />
for(j = 0; j < 120; j+=5)<br />
{<br />
writeChar('\n');<br />
writeInteger(j, DEC);<br />
writeChar('\n');<br />
uint8_t i = 0;<br />
for(i = 0; i < 199; i++)<br />
{<br />
read_Register_30();<br />
<br />
feld_IR[i] = IR_wert[0];<br />
<br />
writeIntegerLength(feld_IR[i],DEC,4);<br />
if(i % 12 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
mSleep(20+j);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
/**************************/<br />
uint8_t key_1 = getMultiIOPressedButtonNumber();<br />
key_1 = getMultiIOPressedButtonNumber();<br />
if(key_1 != 0) break;<br />
<br />
/**************************/<br />
}<br />
<br />
break;<br />
}<br />
}<br />
<br />
<br />
}<br />
<br />
<br />
return 0;<br />
}<br />
/***************************************************************************/<br />
</pre><br />
<br />
Die case1 - case4 werden mit den Tasten 1-4 der multiIO gestartet...<br />
<br />
<br />
Man sollte vor dem Test die Werte der beiden Potis auf der Blinkerplatine einstellen und die Werte festhalten. Ich habe zwei einigermassen funktionierende Blinkfrequenzen der Blinkschaltung gefunden und etwas ausgiebiger getestet, es gibt sicher noch einige mehr:<br />
<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Frequenz''' || '''R1''' || '''R2'''<br />
|-<br />
| 2 / Sek. || 1.1k || 3.7k<br />
|-<br />
| 1 / Sek. || 2.7k ||4.1k<br />
|}<br />
<br />
Bessere Ergebnisse habe ich mit der Blinkfrequenz von 2 / Sek. erzielt. Die „Frequenz“ habe ich nur ganz grob mit einer Stoppuhr „eingestellt“<br />
-----------------------------------------------------------------------------<br />
<br />
In der Datei „ir_bake_test_anleitung.c“ sind im wesentlichen zwei Wege enthalten mit denen man wahlweise oder als Kombination die Sende- und Empfangsfrequenz von RP6 und der bake anpassen kann.<br />
<br />
Die grüne LED auf der Senderplatine blinkt bei eingeschalteter bake (und aufgesteckter Blinkerplatine) in etwa 2x pro Sekunde, die rote LED auf der multi-IO Platine (bei einem Abstand der bake von ca. 30cm vom RP6) leuchtet dauernd wenn nichts empfangen wird, erlischt bei Empfang und die grüne LED geht an, dieser Wechsel wiederholt sich regelmäßig oder eben unregelmäßig, je nachdem wie die Sende- und Empfangsfrequenz zusammenpassen. Wenn jemand keine multi-IO platine hat, muss andere LEDs verwenden! <br />
------------------------------------------------------------------------------<br />
Mit case1: kann man durch verändern der Werte von R1 und R2 ein übereinstimmendes, gleichmäßiges Blinken der beiden grünen LEDs (Senderplatine der bake und die multi-IO) einzustellen versuchen. Oder den Wert des mSleep in der Schleife „if (IR_wert[0] == 0)“ versuchsweise verändern, neu compilieren, laden, starten. Das drehen an den Potis ging schneller :-).<br />
------------------------------------------------------------------------------<br />
Mit case2: kann man sich bei einigermaßen eingestelltem Blinken der bake und der LED am RP6 (es ist zunächst gleichgültig ob regelmäßig oder nicht, es müssen nur beide Seiten blinken) die empfangenen Daten (0000 oder 0004) am Terminal ausgeben lassen. In der Schleife wird der Wert für mSleep in 10er steps von 0 bis 120 erhöht und mit den Werten des Register 30 mit ausgegeben. Terminal abspeichern, bearbeiten. Das sollte anschließend so aussehen:<br />
[[Bild: 2014 05 04 terminal IR-bake 1.jpg|thumb|left|Ausgabe IR-bake Software case2]]<br />
<br />
Hier kann man sich dann ein Empfangsmuster, welches einem am regelmäßigsten erscheint aussuchen und die oberhalb dieses Musters stehende Zahl (Wert der mSleep-Pause) in die Schleife von case1 eintragen / aufteilen. Kompilieren, starten und schauen wie das gleichmäßige Blinken nun aussieht...<br />
<br />
Man könnte - je nach ergebnis - die case 2 oder 4 schleife so umschreiben, dass die mSleep-Länge nur z.B. von 40 bis 60 in 1-er Schritten durchlaufen wird, die Ausgabe auswerten und mit case1 verifizieren...<br />
<br />
<br />
<br />
Case3: hier kann die Funktion FollowLine() getestet werden, die nicht unbedingt zu der Einstellung der bake gehört, bei mir aber mit drin ist...<br />
<br />
<br />
Case4: noch frei...<br />
<br />
<br />
<br />
Das ganze (case1 / case2) wiederholt man dann solange, bis man zufrieden ist. Und steigert dabei auch die Entfernung zwischen Sender und Empfänger. Die Ausrichtung des Sender und Empfänger für die Tests wird dann schwieriger, ich half mir bei geringeren Entfernungen mit einem Lineal, dann mit einem an beiden Enden festgeklebten Wollfaden. Hier noch zwei Fotos zum Testaufbau:<br />
<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 05 04 RP6 an messstrecke.JPG|thumb|left|RP6 Messaufbau 5m Entfernung]] || [[Bild: 2014 05 04 bake an messstrecke.JPG|thumb|left|IR-bake Messaufbau 5m Entfernung]]<br />
|}<br />
<br />
===Störungen durch Reflektionen der IR-Signale===<br />
<br />
Bei den Versuchen habe ich festgestellt, dass der RP6 manchmal in Richtung bake fuhr, öfters aber auch in andere Richtungen. Verursacht haben es Reflektionen der IR- Strahlen. Bei der bake habe ich durch Reflektoren an den IR-Dioden versucht die Probleme zu beseitigen:<br />
[[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]]<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
es handelt sich um einen kleinen Reflektor für 5mm LED von CONRAD, zusammengeklebt mit einem 10mm langem Aluminiumrohr D12x1mm<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Den Empfänger am RP6 habe ich mit einem ähnlichem, größerem Reflektor von CONRAD versehen, an die Verhältnisse an der Base-Platine angepasst, zusammengeklebt mit einem 25mm langem Aluminiumrohr D16x1mm sodass er mit einem dickeren doppelseitigem Klebeband direkt vor den IR-Empfänger auf die BASE-Platine aufgeklebt werden konnte. Die Abschirmung ist Innen mit schwarzem Papier ausgelegt, ebenfalls um Reflektionen zu vermeiden.<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: Reflektor IR diode.JPG|thumb|left|Reflektor für IR-Senderdiode]] || [[Bild: Abschirmung IR empfaenger RP6.JPG|thumb|left|RP6 mit Abschirmung am IR-Empfänger]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]] || [[Bild: Abschirmung IR empfaenger-2.JPG|thumb|left|Abschirmung IR-Empfänger Draufsicht]]<br />
|}<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
In der Software (bake_suche) habe ich noch eines gemacht – es werden die empfangenen Signale pro bestimmten Zeitraum gezählt, bei 10 wird angenommen, dass es die bake ist.<br />
<br />
Es ist schwer zu sagen welche von diesen Maßnahmen mehr Erfolg brachte - ich gehe davon aus, dass es die Kombination war – die Reflektionen spielen keine Rolle mehr...<br />
<br />
Übrigens – bei Versuchen draußen habe ich festgestellt, dass offensichtlich durch die „gepulst“ sendenden IR- Dioden die IR- Signale vom Empfänger auch bei Sonnenschein empfangen wurden, offensichtlich werden diese durch die Sonnenstrahlung nicht gestört. Festgestellt habe ich es nicht auf die 5m Entfernung, es waren vielleicht 2 bis 3 Meter...<br />
<br />
===Software===<br />
<br />
Die bake soll bei einer bestimmten Akkuspannung automatisch aufgesucht werden. Das wird durch die Einbindung der Funktion Akkuzustand erreicht, diese wird in der Mainschleife einer beliebigen Software aufgerufen. Die Höhe der Spannung bei der der RP6 die Ladestation suchen soll ist ebenfalls in dieser Funktion einstellbar (if vbat...):<br />
<pre><br />
/*******************************accuzustand***************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
<br />
{<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
<br />
//bake_suche();<br />
<br />
}<br />
}<br />
/********************************************************************************/<br />
</pre><br />
<br />
in dieser Funktion erfolgt dann der Aufruf der Funktion „bake_suche“ (oben noch auskommentiert)<br />
<br />
<pre><br />
/*******************************bake_suche***************************************/<br />
void bake_suche(void)<br />
{<br />
setLEDs(0b0100);<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
read_Register_30();<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
if (IR_wert[0] == 0) stop();<br />
}<br />
read_Register_30();<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
}<br />
}<br />
}<br />
}<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
}<br />
/*********************************************************************************/<br />
</pre><br />
<br />
die lib standard.c: (in der u.a. auch die beiden oberen Funktionen enthalten sind)<br />
<br />
<pre><br />
/*****************************standard.c*******************************************/<br />
<br />
#include "RP6ControlLib.h"<br />
#include "RP6I2CmasterTWI.h"<br />
#include "RP6Control_MultiIOLib.h"<br />
#include "RP6Control_I2CMasterLib.h"<br />
#include "RP6Control_LFSBumperLib.h"<br />
#include "RP6ControlServoLib.h"<br />
#include "RP6Control_OrientationLib.h"<br />
#include "standard.h"<br />
<br />
/***************************variablen************************************************/<br />
<br />
uint8_t RP6data[32];<br />
uint8_t i, j, t;<br />
int16_t x, y, z;<br />
uint8_t temp;<br />
uint8_t IR_wert[1];<br />
<br />
/*********************************I2C Error handler***********************************/<br />
<br />
void I2C_transmissionError(uint8_t errorState)<br />
{<br />
writeString_P("\nI2C ERROR --> TWI STATE IS: 0x");<br />
writeInteger(errorState, HEX);<br />
writeChar('\n');<br />
}<br />
<br />
/************************ Write a floating point number to the LCD or terminal. ******/<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeStringLCD(&buffer[0]);<br />
}<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec)<br />
{char buffer[width + 1];<br />
dtostrf(number, width, prec, &buffer[0]);<br />
writeString(&buffer[0]);<br />
}<br />
<br />
/**************************************accuspannungsanzeige*****************************/<br />
<br />
void accuspannung(void) // accuspannung ausgeben<br />
{<br />
<br />
multiio_init();<br />
LTC2990_measure();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" accu: ");<br />
writeDoubleLCD(vbat, 4, 1);<br />
writeStringLCD( " V");<br />
}<br />
<br />
/*********************************acculadungsanzeige**********************************/<br />
<br />
void acculadung(void)<br />
{<br />
<br />
clearLCD();<br />
setCursorPosLCD(0, 0);<br />
writeStringLCD(" ADC2: ");<br />
uint16_t adc2 = readADC(ADC_2); // Read ADC Channel 2<br />
setCursorPosLCD(0, 9);<br />
writeIntegerLCD(adc2, DEC);<br />
if (adc2 > 650)<br />
{<br />
setCursorPosLCD(2, 0);<br />
writeStringLCD(" ladespannung ok ");<br />
setMultiIOLED1(1);<br />
<br />
}<br />
}<br />
<br />
/*************************************accuzustand****************************************/<br />
<br />
void accuzustand(void) // accuspannung abfragen und signalisieren<br />
{<br />
<br />
LTC2990_measure();<br />
if (vbat < 6.5)<br />
{<br />
buzzer(330);<br />
mSleep(200);<br />
buzzer(330);<br />
mSleep(200);<br />
//bake_suche();<br />
}<br />
<br />
}<br />
<br />
/***************************readAllRegister********************************************/<br />
<br />
void readAllRegisters(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 0);<br />
I2CTWI_readBytes(I2C_RP6_BASE_ADR,RP6data, 31);<br />
<br />
writeString_P("\nREADING ALL RP6 REGISTERS:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(RP6data[i],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/**********************print_register_30*********************************************/<br />
<br />
void print_Register_30(void)<br />
{<br />
<br />
writeString_P("\nPRINTING REGISTER 30:");<br />
uint8_t i = 0;<br />
for(i = 0; i < 31; i++)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
if(i % 8 == 0)<br />
writeChar('\n');<br />
else<br />
writeString_P(" | ");<br />
writeChar('#');<br />
writeIntegerLength(i,DEC,2);<br />
writeChar(':');<br />
writeIntegerLength(IR_wert[0],DEC,3);<br />
}<br />
writeChar('\n');<br />
}<br />
<br />
/*****************************read_register_30**************************************/<br />
<br />
void read_Register_30(void)<br />
{<br />
I2CTWI_transmitByte(I2C_RP6_BASE_ADR, 30);<br />
IR_wert[0] = I2CTWI_readByte(I2C_RP6_BASE_ADR);<br />
}<br />
<br />
<br />
/********************************watchdog_request********************************/<br />
<br />
void watchDogRequest(void)<br />
{<br />
static uint8_t heartbeat2 = false;<br />
if(heartbeat2)<br />
{<br />
clearPosLCD(1, 14, 1);<br />
heartbeat2 = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(1, 14);<br />
writeStringLCD_P("#");<br />
heartbeat2 = true;<br />
}<br />
}<br />
<br />
<br />
/********************** Heartbeat function************************************/<br />
<br />
void task_LCDHeartbeat(void)<br />
{<br />
if(getStopwatch1() > 500)<br />
{<br />
static uint8_t heartbeat = false;<br />
if(heartbeat)<br />
{<br />
clearPosLCD(0, 15, 1);<br />
heartbeat = false;<br />
}<br />
else<br />
{<br />
setCursorPosLCD(0, 15);<br />
writeStringLCD_P("*");<br />
heartbeat = true;<br />
}<br />
setStopwatch1(0);<br />
}<br />
}<br />
<br />
<br />
<br />
/********************************bake_suche**************************************/<br />
void bake_suche(void)<br />
{<br />
<br />
setLEDs(0b0100);<br />
<br />
writeString_P("bake_suche_1 \n");<br />
writeChar('\n');<br />
initRP6Control();<br />
initLCD();<br />
startStopwatch3();<br />
t=0;<br />
<br />
do<br />
{<br />
if(getStopwatch3() > 50)<br />
{<br />
<br />
read_Register_30();<br />
<br />
if (IR_wert[0] !=0)<br />
{<br />
setMultiIOLED1(1);<br />
setMultiIOLED1(0);<br />
rotate(80, RIGHT, 5, false);<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0) stop();<br />
<br />
}<br />
read_Register_30();<br />
<br />
if (IR_wert[0] == 0)<br />
{<br />
x = getStopwatch3();<br />
setMultiIOLED3(1);<br />
setMultiIOLED3(0);<br />
if (t<10)<br />
{<br />
t++;<br />
<br />
if (t == 10)<br />
{<br />
y = getStopwatch3();<br />
z = y-x;<br />
<br />
writeInteger(x, DEC);<br />
writeChar('\n');<br />
writeInteger(y, DEC);<br />
writeChar('\n');<br />
writeInteger(z, DEC);<br />
writeChar('\n');<br />
<br />
t=0;<br />
setStopwatch3(0);<br />
if (z< 600)<br />
{<br />
move(100, FWD, DIST_MM(100), false);<br />
setStopwatch3(0);<br />
t=0;<br />
mSleep(400);<br />
}<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
}<br />
}<br />
<br />
}<br />
while(!bumper_left && !bumper_right);<br />
stop();<br />
<br />
}<br />
<br />
<br />
/************************calculateDetailDir*************************************/<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading)<br />
{<br />
dir[1] = ' ';<br />
dir[2] = '\0';<br />
dir[3] = '\0';<br />
<br />
if ((heading <= 11) || (heading >=349)) dir[0] = 'N';<br />
if ((heading >= 12) && (heading <= 33)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 34) && (heading <= 56)) {dir[0] = 'N'; dir[1] = 'E';}<br />
if ((heading >= 57) && (heading <= 78)) {dir[0] = 'E'; dir[1] = 'N'; dir[2] = 'E';}<br />
if ((heading >= 79) && (heading <= 101)) dir[0] = 'E';<br />
if ((heading >= 102) && (heading <= 123)) {dir[0] = 'E'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 124) && (heading <= 146)) {dir[0] = 'S'; dir[1] = 'E';}<br />
if ((heading >= 147) && (heading <= 168)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'E';}<br />
if ((heading >= 169) && (heading <= 191)) dir[0] = 'S';<br />
if ((heading >= 192) && (heading <= 213)) {dir[0] = 'S'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 214) && (heading <= 236)) {dir[0] = 'S'; dir[1] = 'W';}<br />
if ((heading >= 237) && (heading <= 258)) {dir[0] = 'W'; dir[1] = 'S'; dir[2] = 'W';}<br />
if ((heading >= 259) && (heading <= 281)) dir[0] = 'W';<br />
if ((heading >= 282) && (heading <= 303)) {dir[0] = 'W'; dir[1] = 'N'; dir[2] = 'N';}<br />
if ((heading >= 304) && (heading <= 326)) {dir[0] = 'N'; dir[1] = 'W';}<br />
if ((heading >= 327) && (heading <= 348)) {dir[0] = 'N'; dir[1] = 'N'; dir[2] = 'W';}<br />
}<br />
<br />
</pre><br />
<br />
die lib standard.h:<br />
<br />
<pre><br />
/******************************standard.h*****************************************/<br />
<br />
#ifndef STANDARD_H_<br />
#define STANDARD_H_<br />
<br />
<br />
void writeDoubleLCD(double number, uint8_t width, uint8_t prec);<br />
<br />
void writeDouble(double number, uint8_t width, uint8_t prec);<br />
<br />
void accuspannung(void);<br />
<br />
void acculadung(void);<br />
<br />
void accuzustand(void);<br />
<br />
void readAllRegisters(void);<br />
<br />
void print_Register_30(void);<br />
<br />
void read_Register_30(void);<br />
<br />
void watchDogRequest(void);<br />
<br />
void task_LCDHeartbeat(void);<br />
<br />
void bake_suche(void);<br />
<br />
void calculateDetailDir(char *dir, uint16_t heading);<br />
<br />
<br />
#endif /*STANDARD_H_*/<br />
<br />
</pre><br />
<br />
===Bauteilebedarf===<br />
{| {{Blauetabelle}}<br />
|-<br />
| '''Artikel''' || '''Wert''' || '''Name''' || '''Anzahl'''<br />
|-<br />
| Kondensator || 10nF || C1 || 1<br />
|-<br />
| Kondensator || 10uF 16V || C2|| 1<br />
|-<br />
| Kondensator || 0.1uF || C3 || 1<br />
|-<br />
| Kondensator || 10uF 25V || C4 || 1<br />
|-<br />
| Kondensator || 1nF || C5 || 1<br />
|-<br />
| Diode || 1N4001 || D1, D2 || 2<br />
|-<br />
| Timer || NE555N || IC1, IC2 || 2<br />
|-<br />
| IR-Diode || LD 271L || IR1, IR2, IR3, IR4, IR5 || 5<br />
|-<br />
| LED 3mm rot || || LED1 || 1<br />
|-<br />
| LED 3mm grün || || LED2 || 1<br />
|-<br />
| Transistor || 2N2222A || Q1, Q2 || 2<br />
|-<br />
| Potentiometer || 100kΩ 64W || R1, R2 || 2<br />
|-<br />
| Potentiometer || 5kΩ, 64W || R6 || 1<br />
|-<br />
| Widerstand || 1kΩ 0.1W || R3, R15|| 2<br />
|-<br />
| Widerstand || 4.7kΩ 0.1W || R4 || 1<br />
|-<br />
| Widerstand || 47Ω 0.1W ||R5, R7 || 2<br />
|-<br />
| Widerstand || 22Ω 0.1W || R8, R9, R10, R11, R12 || 5<br />
|-<br />
| Widerstand || 2.2kΩ 0.1W || R13 || 1<br />
|-<br />
| Widerstand || 10kΩ 0.1W || R14 || 1<br />
|}<br />
<br />
===videos===<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 85cm<br />
<br />
http://youtu.be/fpGOCCoK33o<br />
<br />
Testaufbau für case1, Frequenz 1 / Sek. Entfernung 5m.<br />
<br />
Unregelmäßiger Empfang (Wechsel der multi-IO LEDs von rot auf grün)<br />
<br />
http://youtu.be/JfxO0yqqR7g<br />
<br />
Testaufbau für case1, Frequenz 2 / Sek. Entfernung 6m. Der Empfang ist besser<br />
<br />
http://youtu.be/nV88c8zB5t4<br />
<br />
Testaufbau für case2, Entfernung 85cm <br />
<br />
http://youtu.be/nsxvsr4du6Y<br />
<br />
Suche der IR-bake<br />
<br />
http://youtu.be/HqvyWI0y8Z0<br />
<br />
===Wie geht’s weiter?===<br />
<br />
Die Funktion „suche_bake“ ist jetzt so ausgelegt, dass der RP6, nachdem er die bake gefunden hat per Bumper stehen bleibt. Das werde / würde ich so ändern, dass er<br />
<br />
* zwar immer wieder überprüft ob er immer noch in Richtung bake fährt, aber auf die Funktion „fahre bis schwarze Linie“ umschaltet<br />
* beim erreichen der Linie in der einfachen Version per Definition nach rechts (oder halt links) um 90° dreht und auf „followline“ umschaltet und folgt der Linie, bis er eine schwarze Querlinie findet. Sprechen vorher die Bumper an, war rechts die falsche Richtung, er ist gegen die Wand gefahren. Er setzt also einen Stück zurück, wendet um 180° und folgt der Linie solange zurück bis er die schwarze Querlinie gefunden hat<br />
* in der komfortableren Version weiß der RP6 von seinem Gyro ob er vom Nordost oder Südsüdwest kommt und kann sich beim Finden der Linie entsprechend sofort für Links- oder Rechtsdrehung entscheiden, da er das ja anhand der gespeicherten Richtung in der er die bake vorher „gesehen hat“ erkennt<br />
* in beiden Fällen hat er nun die senkrecht zur Wand führende Linie gefunden und weiß ob die Wand rechts oder links liegt, dreht auf die Linie und fährt den bekannten Abstand zu Ladestation<br />
* lädt....<br />
<br />
== IR-bake 1.1 für RP6 ==<br />
<br />
<br />
===IR-bake kpl.===<br />
Bei der Bake 1.1 habe ich die beiden Leiterplatten zusammengefasst und statt zwei NE555 einen NE556 verwendet. Dadurch ist die Bake schlanker geworden, die Funktion ist die gleiche. Die Befürchtung, die Einstellung der Bake würde etwas komplizierter oder ungünstiger werden, haben sich nicht bewahrheitet...<br />
<br />
<br />
hier noch ein paar Fotos:<br />
<br />
{| {{Blauetabelle}}<br />
| [[Bild: 2014 10 22 breadboard bake 1 1 ne556.JPG|thumb|left|Breadboardaufbau der Bake 1.1]] || [[Bild: 2014 10 31 ne556.JPG|thumb|left|NE556 mit direkt aufgelöteten Bauteilen]] || [[Bild: 2014 10 31 bake 1 1 kpl.JPG|thumb|left|IR-bake 1.1 kpl.]] || [[Bild: 2014 07 07 vergleich 555 556-1.jpg|thumb|left|Vergleich Bake 1.0 und Bake 1.1 - hier noch mit 6 IR-LEDs in reihe]]<br />
|}<br />
<br />
====Stromlaufplan kpl.====<br />
<br />
<br />
<center>[[Bild: 2014 10 08 hochkant ir bake 1 1.jpg|600px|none|Stromlaufplan IR-bake 1.1 kpl.]] </center><br />
<br />
== Navigation mit Hilfe von zwei baken (1.0 und 1.1) ==<br />
<br />
Baustelle...<br />
<br />
<br />
===Autor===<br />
--[[Benutzer:Georg|inka]] 16:20, 15. Mai 2014 (CET)<br />
<br />
[[Kategorie:Elektronik]]<br />
[[Kategorie:Projekte]]</div>Inka