est Bewegung durch eine 3D-Landschaft mit ... - David Zaadstra
est Bewegung durch eine 3D-Landschaft mit ... - David Zaadstra
est Bewegung durch eine 3D-Landschaft mit ... - David Zaadstra
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>est</strong><br />
Alexander Fuchs<br />
<strong>David</strong> <strong>Zaadstra</strong><br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong><br />
<strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong><br />
Steuerung <strong>durch</strong><br />
Fernbedienung<br />
Projektarbeit 2002<br />
Berufskolleg<br />
Elektronikschule Tettnang
<strong>est</strong><br />
Alexander Fuchs, BKIK2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, BKIK2/1<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong><br />
<strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong><br />
Steuerung <strong>durch</strong><br />
Fernbedienung<br />
Betreuer:<br />
Projektarbeit 2002<br />
Berufskolleg<br />
Herr Hambsch<br />
Herr Czok<br />
Elektronikschule Tettnang<br />
Oberhofer Str. 25, 88069 Tettnang, http://www.elektronikschule.de
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Inhaltsverzeichnis<br />
Vorwort 3<br />
1. Zusammenfassung 4<br />
1.1 Deutsch<br />
1.2 English (summary)<br />
2. Arbeitsprozessbericht 6<br />
3. Projektaufbau 7<br />
3.1 Blockschaltbild<br />
4. Fernbedienung 8<br />
4.1 Die Signale der Fernbedienung<br />
4.2 Die Infrarot-Protokolle<br />
4.2.1 RC-5<br />
4.2.2 RECS80<br />
4.2.3 NEC<br />
4.3 Die Empfängerdiode SFH 5110<br />
5. Dekodierung der Signale <strong>mit</strong> dem Mikrocontroller 14<br />
5.1 Der einzulesende Datensatz<br />
5.2 Das Assemblerprogramm<br />
5.2.1 Funktion<br />
5.2.2 PAP<br />
5.2.3 Code<br />
6. Kommunikation Mikrokontroller -> PC 46<br />
6.1 Empfang der Daten <strong>mit</strong> den Communications-Funktionen des Win32-API<br />
6.2 Die MTV24-Klasse<br />
7. Die ManiaTech <strong>3D</strong>-Engine 50<br />
7.1 Einführung in (Echtzeit-)<strong>3D</strong>-Grafik<br />
7.1.1 Polygone<br />
7.1.2 Darstellung von Dreiecken<br />
7.1.3 Transformationen<br />
7.1.4 Texturen und Farben<br />
7.1.5 Licht<br />
7.1.6 Zeichnen („Rendern“) von Grafik<br />
7.2 Struktur der Engine<br />
7.3 Arbeitsweise der Engine<br />
8. Die <strong>Landschaft</strong> 60<br />
8.1 Das Optionsmenü<br />
8.2 Laden der <strong>Landschaft</strong><br />
8.3 Erzeugung der <strong>3D</strong>-Daten<br />
8.4 Der Himmel<br />
8.5 Der Demomodus<br />
- 1 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.6 Die Kontrollen<br />
8.6.1 Fernbedienung<br />
8.6.2 Tastatur<br />
8.7 Das Hauptprogramm<br />
8.7.1 Das Struktogramm<br />
8.7.2 Der Code<br />
9. Schlussbemerkung 79<br />
10. Anhang 80<br />
10.1 Literaturverweise<br />
10.2 Datenblatt SFH 5110<br />
- 2 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Vorwort<br />
Nachdem wir davon ausgehen, dass die meisten Leser prinzipiell kein Vorwort lesen, halten<br />
wir uns so kurz wie möglich, um der Dokumentation unseres Projektes nicht im Wege zu<br />
stehen (und in der Hoffnung, dass das Vorwort zumind<strong>est</strong> überflogen wird.)<br />
Die Idee zu unserem Projekt hat sich recht schnell entwickelt. Da wir uns sowieso schon<br />
vorher <strong>mit</strong> <strong>3D</strong>-Grafik beschäftigt hatten, war der Entschluss, <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> zu<br />
programmieren, schnell gefasst. Um aber <strong>eine</strong> größere Bandbreite unseres Fächerbereiches<br />
abzudecken, entschieden wir uns, zusätzlich auch Anwendungen aus der Elektronik und der<br />
Informationstechnik <strong>mit</strong> einzubeziehen. So stand also der Entschluss, <strong>mit</strong>tels Steuerung <strong>durch</strong><br />
<strong>eine</strong> kabellose Bedieneinheit <strong>eine</strong> virtuelle <strong>3D</strong>-Welt zu bewegen.<br />
Dies kann heute viele verschiedene Anwendungen in Industrie, Heim- und<br />
Präsentationstechnik, wie z.B. Wettersimulationen oder Modelldarstellungen von<br />
Fahrzeugprototypen, finden.<br />
Danke an Daniel „sirleto“ Renkel, der die Texturen und die Höhenkarte für die <strong>Landschaft</strong> zur<br />
Verfügung stellte, sowie an Jason Shankel, dessen Filterfunktionen im <strong>Landschaft</strong>scode zum<br />
Einsatz kommen.<br />
- 3 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
1. Zusammenfassung<br />
1.1 Deutsch<br />
Das Projekt ist in zwei Teilen zu sehen: Der Empfang und die Dekodierung der<br />
Fernbedienungssignale <strong>mit</strong> dem Mikrocontroller und die <strong>3D</strong>-<strong>Landschaft</strong> auf dem PC. Die IR-<br />
Signale der Fernbedienung werden <strong>durch</strong> das IR-Empfängerbauteil SFH5110 erkannt, das<br />
Datensignal vom Trägersignal getrennt und als TTL-Impulsfolge an den Mikrocontroller<br />
80C535 weitergegeben. Dieser misst die Impulslängen des Signals, speichert sie, wandelt sie<br />
in ein internes Format um und vergleicht dieses <strong>mit</strong> <strong>eine</strong>m Datensatz von Signalen, die zuvor<br />
von uns aufgenommen wurden. Wurde das Signal erkannt, wird die Nummer des Signals per<br />
V24 an den COM1-Port des PCs gesendet.<br />
Der Tastencode wird vom Windowsprogramm <strong>mit</strong> den Windows-Communications-<br />
Funktionen empfangen. Den Nummern sind verschiedene Aktionen zugeordnet, z.B. vorwärts<br />
bewegen oder rechts drehen. So kann man sich <strong>durch</strong> die dreidimensionale <strong>Landschaft</strong><br />
bewegen. Diese <strong>Landschaft</strong> wird <strong>durch</strong> <strong>eine</strong> Höhenkarte repräsentiert, die aus <strong>eine</strong>r Bitmap<br />
ausgelesen wird. Diese Höhendaten werden in <strong>3D</strong>-Daten umgewandelt und <strong>mit</strong>tels der<br />
ManiaTech <strong>3D</strong>-Engine 1 darg<strong>est</strong>ellt. Diese Engine basiert auf Direct<strong>3D</strong> und ermöglicht die<br />
Darstellung und Verwaltung der <strong>3D</strong>-Grafik, ohne aber direkte Interaktion <strong>mit</strong> der<br />
darunterliegenden API zu erfordern. Das PC-Programm stellt ausserdem beim Start ein<br />
Optionsmenü zur Verfügung, das <strong>eine</strong> Konfiguration der Grafikeinstellungen erlaubt.<br />
1 Unter <strong>eine</strong>r (<strong>3D</strong>-)Engine versteht man im Allgem<strong>eine</strong>n <strong>eine</strong> Ansammlung von Klassen und/oder Funktionen,<br />
die in <strong>eine</strong>m System <strong>mit</strong>einander interagieren, um b<strong>est</strong>immte Aufgaben (z.B. die Darstellung von <strong>3D</strong>-Grafik) zu<br />
vereinfachen, zu abstrahieren (von darunter liegenden APIs oder dem OS) und zu systematisieren. Der<br />
Unterschied zu <strong>eine</strong>r Library b<strong>est</strong>eht im Wesentlichen darin, dass Interaktion zwischen den Funktionen/Klassen<br />
b<strong>est</strong>eht, sodass das Programm zu <strong>eine</strong>m großen Teil von der Engine angetrieben wird. Bei <strong>eine</strong>r Library dagegen<br />
liegt die Hauptarbeit beim Programm, es wird lediglich Funktionalität aus der Library verwendet.<br />
Es sei darauf hingewiesen, dass es auch andere Interpretationen des Begriffs Engine gibt, von denen sich diese<br />
Definition <strong>durch</strong>aus unterscheiden kann.<br />
- 4 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
1.2 English (summary)<br />
The project can be viewed in two parts: firstly the reception and decoding of remote signals<br />
with the help of a microcontroller, secondly the <strong>3D</strong>-scenery on the PC monitor. The IRsignals<br />
of the remote control are detected and interpreted by the IR receiver component 'SFH<br />
5110', which then separates the data signal from the carrier signal and subsequently trans<strong>mit</strong>s<br />
it by way of a TTL pulse sequence to the micro controller ‘80C535’. After saving the data the<br />
micro controller measures impulse duration, transforms them into an internal format, and<br />
compares it to a data set of signals which have been recorded prior to this process. After<br />
having been interpreted , the number of the signal will be sent to COM1 port of the PC by<br />
way of V24.<br />
The keycode is received by the windows program with the help of the Windows<br />
communications functions. Various actions are assigned to a variety of numbers, e.g. forward<br />
movement or right-hand turning. This enables a user to 'move' in a three-dimensional<br />
landscape. This landscape is represented by a heightmap, which is read out from a bitmap.<br />
The altitude information is transformed into <strong>3D</strong> data and presented by the ManiaTech <strong>3D</strong>engine<br />
1 . This engine is based on Direct<strong>3D</strong> and per<strong>mit</strong>s the presentation and management of<br />
<strong>3D</strong> graphics without needing direct interaction with the underlying API. Apart from that the<br />
program offers an option menu at startup, which allows configuration of the graphic settings.<br />
1 A (<strong>3D</strong>-)engine is commonly understood as a set of classes and/or functions which interact in a system to<br />
simplify, abstract (from underlying APIs or the OS) and systemise certain tasks (e.g. displaying <strong>3D</strong> graphics).<br />
The difference to a library is that there is interaction between the functions/classes so that the program is largely<br />
driven by the engine. Contrary when using a library the main work still has to be done by the program; it merely<br />
uses functionality from the library. Note that there are different interpretations of the term engine which may<br />
differ from this definition.<br />
- 5 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
2. Arbeitsprozessbericht<br />
Das Projekt ist in zwei großen Teilen anzusehen, für die je <strong>eine</strong>r von uns zuständig war: Herr<br />
Fuchs für die Fernbedienung und Herr <strong>Zaadstra</strong> für die <strong>Landschaft</strong> und den übrigen PC-Code.<br />
Jedoch haben wir uns auch teilweise <strong>mit</strong> der Arbeit des Anderen befasst.<br />
Hier <strong>eine</strong> chronologisch sortierte Auflistung unserer Tätigkeiten:<br />
Wann: Wer: Was:<br />
6.2.2002 <strong>Zaadstra</strong> Definition der MTLandscape-Klasse<br />
6.2.2002 Fuchs Experimente zum Empfang des IR-Signals <strong>mit</strong>tels<br />
schuleigener (und nicht geeigneter) IR-Bauteile<br />
8.-13.2.2002 <strong>Zaadstra</strong> erster Prototyp der <strong>Landschaft</strong>, Laden der rohen<br />
Vertexdaten<br />
13.2.2002 Fuchs Suchen nach anderen Lösungen zum Empfang des<br />
Signals SFH5110<br />
16.2.2002 <strong>Zaadstra</strong> „Rundflug“ über die <strong>Landschaft</strong><br />
20.2.2002 Fuchs Aufnehmen und Drucken der Signale der Fernbedienung<br />
22. - 23.2.2002 <strong>Zaadstra</strong> Hinzufügen der Vertexfarben basierend auf Höhe<br />
27.2.2002 Fuchs Entwicklung der Grundkonstruktion des Assembler-<br />
Programms<br />
2.3.2002 <strong>Zaadstra</strong> Tastatursteuerung<br />
6.3.2002 Fuchs Erweiterung des Assembler-Programms<br />
13. + 16.3.2002 <strong>Zaadstra</strong> t<strong>est</strong>weise Programmierung des Optionsfensters <strong>mit</strong> MFC<br />
13.3.2002 Fuchs Debuggen und T<strong>est</strong>en des Programms und der Makros<br />
20.3.2002 <strong>Zaadstra</strong> Hinzufügen der <strong>Landschaft</strong>stexturen<br />
20.3.2002 Fuchs Debuggen und T<strong>est</strong>en des Programms und der Makros<br />
22. + 23.3.2002 <strong>Zaadstra</strong> Programmierung des Himmels<br />
29. - 31.3.2002 <strong>Zaadstra</strong> Entwicklung der MTV24-Klasse<br />
6. - 13.4.2002 <strong>Zaadstra</strong> Programmierung des Demomodus<br />
1. - 12.4.2002 Fuchs Dokumentation, Abschnitte 3-5<br />
1. - 12.4.2002 <strong>Zaadstra</strong> Dokumentation, Abschnitte 6-8<br />
13. + 14.4. 2002 beide Dokumentation, übrige Abschnitte<br />
- 6 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
3. Projektaufbau<br />
Die IR-Fernbedienung sendet per IR-Diode <strong>eine</strong> auf <strong>eine</strong> Trägerfrequenz aufmodulierte<br />
Signalfolge, die <strong>durch</strong> die IR-Empfängerdiode SFH 5110 erkannt und per TTL-Signal auf<br />
Port 1.1 des Mikrocontrollers 80C535 eingespeist wird.<br />
Dieser ist so programmiert, dass er die Signale der Fernbedienung dekodiert, vergleicht,<br />
erkennt und ein Steuerzeichen per V24-Protokoll an den COM1-Port des PCs sendet.<br />
Auf dem PC wird <strong>durch</strong> selbst entwickelte Routinen innerhalb des Programmaufbaus der <strong>3D</strong>-<br />
Engine auf die Communications - Funktionen der Win32-API zugegriffen, die ausgelesenen<br />
Daten werden dem Programmablauf der <strong>3D</strong>-Engine zur Verfügung g<strong>est</strong>ellt.<br />
3.1 Blockschaltbild<br />
- 7 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
4. Fernbedienung<br />
Um unser Projekt „interaktiv“ zu g<strong>est</strong>alten und um den Anwender unabhängig von<br />
Computerperipherie (Maus, Tastatur) zu machen, entschieden wir uns, die Ansteuerung der<br />
Grafik-Engine, wie unter Punkt 3.1 zu erkennen ist, über <strong>eine</strong> externe Infrarotfernbedienung<br />
abzuwickeln.<br />
In diesem Fall verwenden wir die Infrarot-Fernbedienung <strong>eine</strong>s handelsüblichen<br />
Satellitenreceivers vom Typ UFD200 der Marke Kathrein, welche k<strong>eine</strong>s der unter Punkt 4.2<br />
beschriebenen Protokolle unterstützt. Wir entschieden uns bewusst für <strong>eine</strong> solche, nicht<br />
programmierbare Fernbedienung, um unser Assemblerprogramm des Mikrocontrollers<br />
speziell für diese Fernbedienung abstimmen zu können, und um zu zeigen, dass man auf diese<br />
Art und Weise der Dekodierung nicht auf IR-Fernbedienungen angewiesen ist, die <strong>eine</strong>s der<br />
öffentlich standardisierten Protokolle unterstützen.<br />
4.1 Die Signale der Fernbedienung<br />
Bei den Signalen der verwendeten Kathrein IR-Fernbedienung muss zwischen 2 Tasten- und<br />
so<strong>mit</strong> auch 2 unterschiedlichen Signalarten unterschieden werden:<br />
- Signale generiert <strong>durch</strong> einfachen Tastendruck<br />
- Signale generiert <strong>durch</strong> dauerhaften Tastendruck<br />
Dabei sind nur 4 von insgesamt 26 Tasten in der Lage, <strong>eine</strong>n dauerhaften Tastendruck zu<br />
senden, nämlich die „Lautstärke hoch“, die „Lautstärke runter“, die „Kanal hoch“ und die<br />
„Kanal runter“ Taste. Dies erscheint logisch, da man bei der Bedienung <strong>eine</strong>s<br />
Satellitenreceivers die Zahlentasten immer nur einmal kurz drückt, bei der Regelung der<br />
Lautstärke jedoch länger auf der entsprechenden Taste bleibt.<br />
Diese Eigenschaft wird bei der Ansteuerung der <strong>3D</strong>-Engine genutzt, indem die Tasten, die nur<br />
einfachen Druck zulassen, als Steuertasten für Einstellungen der Engine nutzen werden.<br />
Tasten, die jedoch zusätzlich auch das Signal für das „Gedrückthalten“ der Taste senden<br />
werden für Funktionen der Engine wie „vor“, „zurück“, „links drehen“ und „rechts drehen“<br />
verwendet.<br />
Aufgrund der Komplexität der Signale von „Einfachtasten“ sind nun unterhalb die 4<br />
Signalfolgen, der genannten „Dauertasten“ bei Gedrückthalten abgebildet:<br />
- 8 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Lautstärke hoch:<br />
Lautstärke runter:<br />
- 9 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Kanal hoch:<br />
Kanal runter:<br />
- 10 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Mehr zu den Signalen und ihrer Interpretation im Assemblerprogramm ist unter Punkt 5<br />
beschrieben.<br />
4.2 Infrarot-Protokolle<br />
Fernbedienungen verwenden im Allgem<strong>eine</strong>n Infrarotdioden, die bei <strong>eine</strong>r Wellenlänge von<br />
950 nm arbeiten. Die eigentliche Information wird nach unterschiedlichen Verfahren auf <strong>eine</strong><br />
Trägerfrequenz zwischen 30 kHz und 40 kHz aufmoduliert. Durch die Verwendung <strong>eine</strong>s<br />
Trägers lässt sich das Nutzsignal besser von Störimpulsen unterscheiden, denn beispielsweise<br />
Leuchtstoffröhren strahlen nicht unerheblich im Infrarot-Bereich.<br />
Die Aufgabe der Empfängerdiode ist es, die Trägerfrequenz und das darauf übertragene<br />
Datensignal zu erkennen. Durch die hohe Empfindlichkeit der Empfänger erreicht man auch<br />
ohne große Sendeleistung <strong>eine</strong> relativ große Reichweite. Da Reflexion an Wänden oft zur<br />
Übertragung ausreicht, ist nicht einmal direkter Sichtkontakt zwischen Sender und Empfänger<br />
nötig.<br />
Wellenlänge und Trägerfrequenz sind leider die einzigen Faktoren, die bei verschiedenen<br />
Herstellern von Fernbedienungen vergleichbar sind. Da<strong>mit</strong> der IR-Empfänger den<br />
Tastendruck auf der Fernbedienung richtig interpretieren kann, müssen die IR-Signale in<br />
geeigneter Weise kodiert sein. Doch bereits bei der Kodierung der Daten gibt es zwischen<br />
verschiedenen Herstellern wesentliche Unterschiede. Die gebräuchlichsten Protokolle für<br />
Infrarotbedienungen sind jedoch RC-5, RECS 80 und NEC.<br />
4.2.1 RC-5<br />
Dieses Verfahren nutzt zur Datenüber<strong>mit</strong>tlung die so genannte ‘biphase’ Kodierung. Dabei<br />
hat jedes Bit ein Zeitfenster, welches in zwei Phasen gleicher Dauer unterteilt ist. Die logische<br />
‘0’ wird <strong>durch</strong> <strong>eine</strong>n Lichtimpuls gefolgt von <strong>eine</strong>r Pause, die logische ‘1’ entsprechend <strong>durch</strong><br />
<strong>eine</strong> Pause gefolgt von <strong>eine</strong>m Lichtimpuls kodiert. Da<strong>durch</strong> ergibt sich ein für RC-5-Systeme<br />
charakteristisches Signal <strong>mit</strong> je zwei unterschiedlichen Impuls- und Pausenlängen. Ein<br />
einzelnes Datenpaket ist unterteilt in ein Startbit, ein Togglebit, fünf Systembits, die das<br />
Empfangsgerät spezifizieren, und sechs Befehlsbits, die die eigentliche Befehlsinformation<br />
enthalten. Das neuere RC-6-Protokoll funktioniert analog, halbiert jedoch die Signallängen,<br />
sodass die Datenpakete länger ausfallen können.<br />
- 11 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
4.2.2 RECS 80<br />
Das RECS-80-Protokoll kodiert einzelne Bits <strong>durch</strong> unterschiedlich lange Pausen, die<br />
Lichtimpulsen konstanter Länge (140,8 µs) folgen. Die für den Bit-Wert entscheidenden<br />
Pausen zwischen den Lichtimpulsen betragen für die ‘0’ 5,06 ms und für die ‘1’ 7,59 ms. Es<br />
werden elf Bits lange Datenpakete verschickt: ein Startbit, ein Togglebit, drei Bits für das<br />
Subsystem und sechs Bits für das Kommando. Obwohl die Datenpakete für die Übertragung<br />
je nach Kommando unterschiedlich lange brauchen, ist die Gesamtlänge <strong>mit</strong> Pause bis zur<br />
nächsten Wiederholung des Signals konstant.<br />
Das Togglebit bei RECS 80 und RC-5 hat <strong>eine</strong> spezielle Funktion. Um besser zwischen<br />
mehreren aufeinander folgenden Tastendrücken und <strong>eine</strong>m einzigen langen Tastendruck auf<br />
der Fernbedienung unterscheiden zu können, wechselt dieses Bit bei jedem Tastendruck<br />
s<strong>eine</strong>n Zustand.<br />
4.2.3 NEC<br />
Der NEC-Standard funktioniert ähnlich wie RECS 80, wobei sich die Länge der Impulse und<br />
Pausen unterscheidet. Die Datenpakete sind 32 Bit lang, aufgeteilt in 16 Bit für Adressierung<br />
und 16 Bit für Daten. Meist sind die letzten 8 Bit dieser Teilpakete das bitweise Komplement<br />
der ersten acht, um Übertragungsfehler erkennen zu können.<br />
Leider existieren neben den drei beschriebenen Standards noch etliche andere Ansätze, die<br />
meist nicht öffentlich dokumentiert sind. So setzt Sony ein Verfahren ein, bei dem die Länge<br />
der Lichtimpulse selbst für den Bitwert entscheidend ist. Viele andere Hersteller setzen<br />
Varianten von RECS 80 und NEC ein, die sich in der Länge der Impulse und Pausen, in der<br />
Anzahl und Semantik der Datenbits und in der Methode, lange Tastendrücke kenntlich zu<br />
machen unterscheiden. Weit verbreitet ist auch das Aussenden <strong>eine</strong>s langen Header-Signals,<br />
welches dem Empfänger erlaubt, sich auf das folgende Signal einzumessen.<br />
- 12 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
4.3 Die Empfängerdiode SFH 5110<br />
Die Empfängerdiode SFH 5110 setzt bei der Aufmodulierung des Nutzsignals auf <strong>eine</strong><br />
Trägerfrequenz an. Ihre Aufgabe ist es, das Trägersignal und das darauf aufmodulierte<br />
Datensignal zu erkennen, voneinander zu unterscheiden und das Datensignal per TTL-Pegel<br />
auszugeben.<br />
Im Bauteil integriert sind Fotodiode, Vorverstärker, automatische Verstärkungsregelung,<br />
Bandpass-Filter und Demodulator. Als Anwendung wird der Empfang von<br />
Fernbedienungssignalen von TV, Videorekordern, HiFi, Satellitenempfängern und CD-<br />
Spielern angegeben.<br />
An dieser Stelle sei auch auf das Datenblatt von SFH 5110/5111 im Anhang unter Punkt 9.1<br />
verwiesen.<br />
- 13 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
5. Dekodierung der Signale <strong>mit</strong> dem Mikrocontroller<br />
Da wie gesagt die verwendete Kathrein Fernbedienung k<strong>eine</strong>s der oben beschriebenen<br />
Protokolle unterstützt, waren wir gezwungen, <strong>eine</strong> eigene Art der Interpretation der Signale zu<br />
entwickeln und im Assemblerprogramm anzuwenden.<br />
Dabei fiel uns auf, dass im Signalverlauf <strong>eine</strong>s Tastendruckes auf der Fernbedienung immer<br />
ein einheitliches Muster auftritt:<br />
einheitliches<br />
Header-Signal<br />
Datenbits<br />
ca. 9 ms ca. 5 ms ca. 1,5 ms ca. 0,5 ms<br />
Zu Beginn des Signalverlaufs wird immer ein bei allen Tasten einheitlich langes Header-<br />
Signal gesendet. Die dann nachfolgenden Datenbits haben immer entweder die Länge 1,5 ms<br />
oder 0,5 ms.<br />
Die Interpretation der Signallängen erfolgt dann nach folgendem Muster:<br />
X X 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 X<br />
X = wird nicht berücksichtigt<br />
0 = Bitzeit 0,5 ms<br />
1 = Bitzeit 1,5 ms<br />
Die so<strong>mit</strong> gewonnene Binärzahlreihenfolge „00000001 01000000“ wird dann in Bytes<br />
eingeteilt und kann so einfach <strong>mit</strong> den eingelesenen Datensätzen verglichen werden.<br />
- 14 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
5.1 Der einzulesende Datensatz<br />
Da<strong>mit</strong> der Mikrocontroller das empfangene Signal interpretieren, vergleichen und erkennen<br />
kann, muss er <strong>eine</strong>n Datensatz, in dem alle Signale der Fernbedienung entschlüsselt und<br />
interpretiert aufgelistet sind, zur Verfügung haben.<br />
Den folgenden Datensatz erhielten wir, indem wir uns <strong>durch</strong> <strong>eine</strong> Variation des Assembler-<br />
Programms die Bitzeiten aller Signale per V24 am PC-Monitor anzeigen ließen:<br />
Ausschnitt aus dem Datensatz:<br />
; Anzahl der Signale<br />
db 27<br />
;Dummy<br />
db 00h, 00h, 00h, 00h, 00h, 00h<br />
; OL2<br />
db 44h, 40h, 00h, 00h, 00h, 00h<br />
Tastenname<br />
; OR2<br />
db 54h, 40h, 00h, 00h, 00h, 00h<br />
; UL2<br />
db 14h, 40h, 00h, 00h, 00h, 00h<br />
; UR2<br />
db 01h, 40h, 00h, 00h, 00h, 00h<br />
In Hexadezimale Form<br />
umgewandelte Bitfolge<br />
Der gesamte Datensatz steht in genau dieser Schreibweise in <strong>eine</strong>r Textdatei und wird am<br />
Ende des Quellcodes eingebunden. Über <strong>eine</strong> Marke kann dann auf die Daten zugegriffen<br />
werden.<br />
- 15 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
5.2 Das Assemblerprogramm<br />
Das Assemblerprogramm ist in 80535-Assembler geschrieben und umfasst, Kommentare<br />
eingeschlossen, etwa 600 Zeilen selbst entwickelten Code.<br />
Die Aufgabe dieses Assemblerprogramms ist es, die Signalfolge, welche von der Diode SFH<br />
5110 auf Port 1.1 des Mikrocontrollers eingespeist wird, zu interpretieren, zu vergleichen und<br />
zu erkennen. Anschließend soll die Nummer des erkannten Signals per V24 an den COM -<br />
Port des PCs gesendet werden.<br />
Das Programm b<strong>est</strong>eht aus<br />
- Hauptprogramm<br />
- Interrupt-Programm<br />
sowie aus den Makros<br />
- WAIT_HIGH<br />
- SAVE<br />
- POSTPROCESSDATA<br />
- benutzt: ENCODE<br />
- COMPARE<br />
- benutzt: COMPAREONE<br />
- SENDAKKU<br />
die per .inc-Datei in das Hauptprogramm inkludiert werden. Entwickelt wurde das Programm<br />
unter der Entwicklungsumgebung µVision der Firma Keil.<br />
5.2.1 Funktion<br />
Um, wie unter Punkt 5 bemerkt, das auf Port 1.1 eingespeiste Signal dekodieren zu können<br />
muss der Mikrocontroller also im Signalverlauf das Header-Signal und die 2<br />
unterschiedlichen Bitzeiten messen, speichern, in Bits umwandeln, vergleichen und erkennen<br />
können. Da hier ein äußerst knappes Timing eingehalten werden muss, werden während des<br />
Signalverlaufs ausschließlich die Impulslängen <strong>durch</strong> den Timer 0 im Interrupt-Modus in<br />
0,1ms – Schritten gemessen, in die Register R0 und R1 addiert und anschließend <strong>durch</strong> das<br />
Makro „SAVE“ in den externen Speicher gespeichert. Die Impulslänge <strong>eine</strong>s Low-Impulses<br />
wird dabei immer in R0, die Länge <strong>eine</strong>s High-Impulses in R1 aufaddiert.<br />
Um immer erst genau bei Beginn <strong>eine</strong>s Signals die Messung zu starten, setzt das Makro<br />
„WAIT_HIGH“ <strong>eine</strong>n ca. 10ms langen High-Impuls voraus, welcher nur zwischen dem<br />
- 16 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Anfang und dem Ende zweier Signale sein kann. Außerdem beendet es sich erst bei<br />
Abbrechen des High-Impulses, also bei Beginn des Headers <strong>eine</strong>s Signals.<br />
Ist ein Signalverlauf komplett vermessen und gespeichert (wird <strong>durch</strong> <strong>eine</strong>n High-Impuls ><br />
5ms erkannt), werden die bis zu 50 einzelnen Bytes, in denen die Impulszeiten gespeichert<br />
sind, <strong>durch</strong> das Makro POSTPROCESSDATA in 6 Bytes nach der unter Punkt 5<br />
beschriebenen Methode dekodiert.<br />
Speicherstelle:<br />
Inhalt:<br />
Länge der Impulse<br />
in 0,1 ms<br />
C000h C0001h C0002h C0003h C0004h<br />
90h 50h 5h 15h 15h<br />
X X 0 1 1<br />
Interpretation in Nullen und Einsen<br />
POSTPROCESSDATA hängt außerdem grundsätzlich 4 Bytes <strong>mit</strong> dem Inhalt „0“ an die<br />
Signale an, da, wie gesagt, 4 der 26 Signale kürzer sind und nur 2 Bytes umfassen.<br />
Stehen also im externen Speicher ab der Adresse C000h die 6 Bytes des empfangenen und<br />
gewandelten Signals, können sie <strong>durch</strong> das Makro COMPARE <strong>mit</strong> den Signalen im<br />
gespeicherten Datensatz verglichen werden. Wird das empfangene Signal erkannt, hinterlässt<br />
COMPARE im Akkumulator die Nummer des Signals (ist gleichzeitig auch die Stelle an der<br />
das Signal im Datensatz steht). Ist das Signal <strong>mit</strong> k<strong>eine</strong>m der gespeicherten Signale identisch<br />
wird 27d im Akku hinterlassen.<br />
Schließlich wird <strong>durch</strong> das Makro SENDAKKU der Inhalt des Akkumulators per V24 an den<br />
COM1 – Port des PCs gesendet und an den Anfang des Programmverlaufs gesprungen.<br />
5.2.2 PAP<br />
- 17 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Hauptprogramm<br />
Start<br />
Start des Programms<br />
SP #80h<br />
Der Stackpointer wird gesetzt<br />
Interrupts<br />
initialisieren<br />
Die Interrupt-Einstellungen werden so gesetzt,<br />
dass Timer 0 in den Interrupt gehen kann<br />
R7 #0<br />
R7 ist der Indikator dafür, ob das Signal zu Ende<br />
ist, oder noch andauert. Es wird im Interrupt-<br />
Programm beschrieben<br />
WAIT_HIGH<br />
Um genau den Anfang des Signals abzupassen<br />
wartet Wait_High auf ein ca. 10ms langes High<br />
Timer 0 starten<br />
TH0 #FFh<br />
TL0 #9Bh<br />
R2 #0<br />
Der Timer 0 wird so initialisiert, dass er genau<br />
0,1ms bis zum Überlauf braucht<br />
In R2 wird die Anzahl der Impulse + Pausen<br />
abgelegt<br />
R1 #0<br />
Jedes mal wenn High anliegt und der Timer<br />
überläuft werden in R1 die High-Impulslängen<br />
hochgezählt<br />
A #0<br />
R0 #0<br />
A indiziert dem Timer, ob er High (R1) oder Low<br />
(R0) hochzählen soll. 0 bedeutet Low, 1 bedeutet<br />
High. A wird <strong>mit</strong> 0 initialisiert, da als erstes ein<br />
langes Low am Anfang des Signals steht<br />
Jedes mal wenn Low anliegt und der Timer<br />
überläuft werden in R0 die Low-Impulslängen<br />
hochgezählt<br />
B R1<br />
Die Länge des letzten High-Impulses wird B<br />
übergeben, da<strong>mit</strong> SAVE sie speichern kann<br />
- 18 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
SAVE<br />
SAVE speichert die Länge des letzten Impulses<br />
im externen Datenspeicher ab C000h<br />
P1.1 = = 0<br />
nein<br />
A #1<br />
ja<br />
Warten, bis der Low-Impuls vorbei ist und<br />
gleichzeitiges hochzählen von R0 im Timer -<br />
Interrupt<br />
Der Low-Impuls ist vorbei, jetzt liegt High an.<br />
Der Timer muss das <strong>durch</strong> A erkennen und da<strong>mit</strong><br />
R1 hochzählen<br />
R1 #0<br />
R1 für das Zählen säubern<br />
B #R0<br />
SAVE<br />
Während des Messens des anliegenden Impulses<br />
ist Zeit, die Länge letzten Low-Impuls zu<br />
speichern<br />
R7 = = 0<br />
ja<br />
nein<br />
Wenn der Timer R7 gesetzt hat, ist das Signal<br />
vorbei und es wird an den Anfang gesprungen<br />
ja<br />
P1.1 = = 1<br />
nein<br />
Liegt das Signal noch an, warten bis der High-<br />
Impuls vorbei ist und messen s<strong>eine</strong>r Länge. Ist der<br />
High-Impuls vorbei, wird wieder zu LOW<br />
gesprungen.<br />
- 19 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Interrupt-Programm<br />
Start<br />
Timerwerte setzen<br />
TH0 #FFh<br />
TL0 #9Bh<br />
Timerwerte neu setzen<br />
A = = 0<br />
ja<br />
R0 R0 + 1<br />
nein<br />
Ist A = 0, so liegt Low an, R0 muss inkrementiert<br />
werden<br />
Ist A = 1, liegt High an und R1 muss<br />
inkrementiert werden<br />
R0 inkrementieren<br />
TF0 0<br />
TF0 für nächsten Interrupt wieder löschen<br />
reti<br />
Zurückspringen ins Hauptprogramm<br />
R1 R1 + 1<br />
High liegt an, also R1 inkrementieren<br />
R1 = = 50<br />
nein<br />
TF0 0<br />
ja<br />
Liegt High schon länger als 5ms an, ist das Signal<br />
vorbei und folgende Routinen werden eingeleitet<br />
Das Signal ist noch nicht vorbei, TF0 für nächsten<br />
Interrupt löschen<br />
reti<br />
Zurücksprung ins Hauptprogramm<br />
- 20 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
EAL 0<br />
Das Signal ist vorbei, nichts muss gemessen<br />
werden, deshalb ausschalten der Interrupts<br />
POSTPROCESSDATA<br />
Dieses Makro wandelt die einzelnen<br />
Impulslängen ins Bits und Bytes und schreibt sie<br />
an C000h<br />
DPTR auf<br />
Datensätze<br />
stellen<br />
Der DPTR wird auf die eingelesen Datensätze<br />
g<strong>est</strong>ellt<br />
A 1. Datensatz<br />
DPTR DPTR + 1<br />
An 1. Stelle steht die Anzahl der Signale, diese<br />
wird in den Akku geschoben, da das folgende<br />
Makro diese benötigt<br />
Für das folgende Makro den DPTR<br />
inkrementieren<br />
COMPARE<br />
SENDAKKU<br />
Dieses Makro vergleicht das empfangene Signal<br />
<strong>mit</strong> den Signalen des eingelesnen Datensatzes und<br />
gibt die Nummer des erkannten Signals im Akku<br />
zurück<br />
Senden der Signalnummer an den PC<br />
R7 #1<br />
Indizieren, dass Signal vorbei ist und ins<br />
Hauptprogramm zurückspringen<br />
- 21 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro WAIT_HIGH<br />
Start<br />
Register<br />
sichern<br />
Dieses Makro wartet auf ein ca. 10ms langes<br />
High, danach wartet es auf den 1. Low-Impuls<br />
<strong>eine</strong>s Signals, so wird garantiert, dass genau der<br />
Anfang des Signals abgepasst wird<br />
Alle verwendeten Register auf den Stack schieben<br />
R0 #0<br />
R1 #0<br />
R0 und R1 stellen <strong>eine</strong>n 16 Bit-Wert dar. R0 ist<br />
das Lowbyte, R1 ist das Highbyte<br />
R0 R0 + 1<br />
Lowbyte inkrementieren<br />
C = = 1<br />
ja<br />
R1 R1 + 1<br />
nein<br />
Ist das Lowbyte übergelaufen, wurde das Cary-<br />
Bit gesetzt. Dann muss zusätzlich das Highbyte<br />
inkrementiert werden<br />
Highbyte inkrementieren<br />
P1.1 = = 1<br />
ja<br />
nein<br />
Kontrolle: Liegt immer noch High an?<br />
Wenn nicht, Rücksprung<br />
nein<br />
R1 = = 4<br />
Wenn R1 = 4 ist, also ca. 10ms vorüber sind muss<br />
nur noch auf das 1. Low <strong>eine</strong>s Signals gewartet<br />
werden<br />
P1.1 = = 1<br />
ja<br />
So lang warten, bis 1. Low <strong>eine</strong>s Signals anliegt<br />
Register<br />
r<strong>est</strong>aurieren<br />
Alle verwendeten Register r<strong>est</strong>aurieren<br />
ENDM<br />
- 22 -<br />
Rücksprung ins Hauptprogramm
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro SAVE<br />
Start<br />
Dieses Makro speichert die im Hauptprogramm<br />
gemessenen Impulslängen im externen<br />
Datenspeicher ab C000h<br />
Register<br />
sichern<br />
Alle verwendeten Register sichern<br />
R2 R2 + 1<br />
In R2 steht die Anzahl der gespeicherten Impuls +<br />
Pausenlängen, deshalb jetzt inkrementieren<br />
DPTR DPTR + 1<br />
Den Datenpointer inkrementieren, da<strong>mit</strong> nächstes<br />
Byte geschrieben werden kann<br />
A B<br />
In B steht der zu speichernde Wert<br />
@DPTR A<br />
Speichern<br />
Register<br />
r<strong>est</strong>aurieren<br />
Alle verwendeten Register r<strong>est</strong>aurieren<br />
ENDM<br />
Rücksprung ins Hauptprogramm<br />
- 23 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro<br />
POSTPROCESSDATA<br />
Start<br />
Register<br />
sichern<br />
Dieses Makro wandelt <strong>mit</strong> Hilfe des Makro<br />
Encode die gespeicherten Impulslängen in Bits<br />
und Bytes und speichert diese an die alte Stelle an<br />
C000h<br />
Alle verwendeten Register sichern<br />
R0 #0<br />
R1 #0<br />
R7 #0<br />
In R0 wird die Anzahl der Impulse + Pausen<br />
gespeichert.<br />
R1 ist der Zähler für die 8 Bits <strong>eine</strong>s Bytes.<br />
R7 ist der 2. dpl-Zeiger.<br />
Alle Register werden gelöscht.<br />
DPTR #C004h<br />
Die ersten 4 Bytes werden übersprungen, da diese<br />
immer gleich und irrelevant sind<br />
R2 R2 - 4<br />
Entsprechend wird auch die Anzahl der zu<br />
wandelnden Impulslängen um 4 verringert<br />
R0 R0 + 1<br />
R0 wird auf die 1. Impulslänge gesetzt<br />
ACC<br />
Der Akku wird <strong>durch</strong> push gesichert<br />
A @DPTR<br />
Die 1. Signallänge wird aus dem externen<br />
Speicher geholt<br />
ENCODE<br />
Dieses Makro setzt das Carry-Bit, wenn der<br />
Impuls <strong>eine</strong> logische 1 darstellt, ansonsten löscht<br />
es dieses<br />
ACC<br />
Der Akku wird <strong>durch</strong> pop r<strong>est</strong>auriert<br />
C A<br />
Das Carry-Bit wird nun in den Akku geshiftet<br />
- 24 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
DPTR DPTR + 1<br />
Der DPTR wird <strong>eine</strong> Impulslänge weitergerückt<br />
R1 R1 - 1<br />
R1 überwacht, dass 8 Bits in ein Byte kommen,<br />
wird also jetzt dekrementiert<br />
R1 = = 0<br />
ja<br />
DPL<br />
nein<br />
Entscheidung: Sind bereits 8 Impulslängen in Bits<br />
gewandelt und in den Akku geshiftet worden?<br />
Wenn ja wird jetzt speichert, wenn nein weiter<br />
gesprungen<br />
Dazu wird der DPL gesichert<br />
DPL R7<br />
und der 2. dpl-Zeiger R7 verwendet<br />
@DPTR A<br />
Das volle Byte wird in den externen Speicher<br />
geschrieben<br />
R7 R7 + 1<br />
Der 2. dpl-Zeiger wird weitergerückt<br />
DPL<br />
Jetzt wird der ursprüngliche DPL wieder<br />
r<strong>est</strong>auriert...<br />
R1 #8<br />
...und R1 wieder 8 geschrieben, da<strong>mit</strong> wieder von<br />
vorn begonnen werden kann, falls noch nicht alle<br />
Impulslängen gewandelt sind<br />
R2 R2 -1<br />
Eine Impulslänge wurde gewandelt, also den<br />
Zähler dekrementieren<br />
R2 = = 0<br />
nein<br />
Entscheidung: Wurden alle Impulslängen<br />
gewandelt? Wenn nicht: noch mal von vorn<br />
ja<br />
- 25 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
ACC<br />
DPH #C0h<br />
Es wurden alle Impulslängen gewandelt. Nun<br />
kann da<strong>mit</strong> begonnen werden, die kürzeren<br />
Signale <strong>durch</strong> Anhängen von Nullen auf gleiche<br />
Länge <strong>mit</strong> den längeren Signalen zu bringen.<br />
Dazu Akku sichern<br />
und den DPTR an das Ende des gewandelten<br />
Signals stellen<br />
DPL R7<br />
R7 #4<br />
Es sollen pauschal 4 Nullen angehängt werden,<br />
egal ob kurzes Signal oder langes. R7 dient als<br />
Zähler<br />
A #0<br />
Die anzuhängende Null in Akku schreiben<br />
@DPTR A<br />
und in den externen Datenspeicher schieben<br />
DPL DPL + 1<br />
Eine Speicherstelle weiter rücken<br />
R7 R7 - 1<br />
Den Zähler dekrementieren<br />
R7 = = 0<br />
ja<br />
Register<br />
r<strong>est</strong>aurieren<br />
nein<br />
und entscheiden, ob alle 4 Nullen geschrieben<br />
wurden<br />
Zum Schluss alle verwendeten Register<br />
r<strong>est</strong>aurieren<br />
ENDM<br />
Rücksprung ins Hauptprogramm<br />
- 26 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro ENCODE<br />
Start<br />
Dieses Makro wird ausschließlich im Makro<br />
Postprocessdata verwendet. Es dient dazu, die 2<br />
unterschiedlichen Impulslängen in Bits zu<br />
kodieren. Dazu wird entweder das Carry-Bit<br />
gesetzt oder nicht. Die Impulslänge wird im Akku<br />
erwartet<br />
A = = #13|#14|#15|#16|#17<br />
ja<br />
Entscheidung:<br />
Ist die Impulslänge etwa 1,5ms lang, Carry<br />
setzen, ansonsten nächster Vergleich<br />
nein<br />
A = = #3 | #4 | #5 | #6 | #7<br />
Entscheidung:<br />
Ist die Impulslänge etwa 0,5ms lang, Carry-Bit<br />
löschen, da es <strong>eine</strong> logische 0 bedeutet<br />
C 0<br />
C 1<br />
ENDM<br />
Rücksprung ins Makro Postprocessdata<br />
- 27 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro COMPARE<br />
Start<br />
Dieses Makro vergleicht <strong>mit</strong> Hilfe des Makros<br />
Compareone die Signale des eingelesenen<br />
Datensatzes <strong>mit</strong> dem empfangenen Signal bei<br />
C000h. Die Anzahl der Signale im Datensatz wird<br />
im Akku erwartet, auch wird erwartet dass der<br />
DPTR auf das 1. Signal zeigt<br />
Register<br />
sichern<br />
Alle verwendeten Register werden gesichert<br />
R0 #0<br />
R0 ist die Nummer des gefundenen Signals und<br />
wird im Hauptprogramm an den PC gesendet<br />
R2 A<br />
Die Anzahl der Signale im Datensatz wird in R2<br />
gesichert<br />
COMPAREONE<br />
Dieses Makro vergleicht das Signal, auf dessen<br />
Anfang DPTR zeigt <strong>mit</strong> dem empfangenen Signal<br />
bei C000h und gibt b = 1 für identisch oder b = 0<br />
für verschieden zurück<br />
R3 B<br />
Das Ergebnis wird in R3 gesichert<br />
R3 = = 0<br />
ja<br />
R0 R0 + 1<br />
nein<br />
Entscheidung: ist das Signal im Datensatz<br />
identisch <strong>mit</strong> dem empfangenen Signal?<br />
Wenn nein, die Nummer des Signals<br />
inkrementieren<br />
DPTR DPTR + 6<br />
Den DPTR 6 Bytes weiterstellen, auf nächstes<br />
Signal im Datensatz<br />
R2 R2 - 1<br />
und die Anzahl der noch zu vergleichenden<br />
Signale dekrementieren<br />
- 28 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
nein<br />
ja<br />
R2 = = 0<br />
A R0<br />
Entscheidung: wurden bereits alle Signale<br />
verglichen? Wenn nein, nächstes Signal<br />
vergleichen<br />
Die Nummer des gefundenen Signals in den Akku<br />
schieben. Wenn das empfangene Signal <strong>mit</strong><br />
k<strong>eine</strong>m der Signale im Datensatz identisch war,<br />
steht jetzt in R0 die 27, da alle 26 Signale<br />
verglichen wurden<br />
Register<br />
r<strong>est</strong>aurieren<br />
Alle verwendeten Register r<strong>est</strong>aurieren<br />
ENDM<br />
Rücksprung ins Hauptprogramm<br />
- 29 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro COMPAREONE<br />
Start<br />
Dieses Makro wird ausschließlich im Makro<br />
Compare verwendet. Es vergleicht immer nur 1<br />
Signal (6 Bytes) des Datensatzes <strong>mit</strong> dem<br />
empfangenen Signal an C000h und setzt dann B =<br />
1, wenn identisch und B = 0, wenn verschieden.<br />
Dieses Makro erwartet den auf den Datensatz<br />
eing<strong>est</strong>ellten DPTR<br />
Register<br />
sichern<br />
Alle verwendeten Register sichern<br />
R1 #6<br />
R1 ist der Zähler für die Bytes, also 6<br />
hineinschreiben<br />
R2 #0<br />
R2 ist der 2. dpl-Zeiger, er wird gebraucht, um<br />
zwischen der dem Datensatz und dem Signal bei<br />
C000h hin- und her zuspringen<br />
A @DPTR<br />
Das 1. Bytes des Signals im Datensatz wird<br />
gelesen<br />
R0 A<br />
...und in R0 gesichert<br />
DPTR DPTR + 1<br />
Den DPTR auf das nächste Byte stellen<br />
DPTR<br />
Und sichern<br />
DPH #C0h<br />
Jetzt wird der DPTR auf das Signal an C000h<br />
g<strong>est</strong>ellt<br />
DPL R2<br />
R2 R2 + 1<br />
Der 2. dpl-Zeiger wird weiterg<strong>est</strong>ellt<br />
A @DPTR<br />
Jetzt wird das 1. Byte des empfangenen Signals<br />
aus dem externen Speicher geholt<br />
- 30 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
DPTR<br />
Der alte DPTR, welcher auf das Signal im<br />
Datensatz zeigt wird r<strong>est</strong>auriert<br />
nein<br />
A = = R0<br />
ja<br />
R1 R1 - 1<br />
Nun wird verglichen ob das Byte des<br />
empfangenen Signals identisch <strong>mit</strong> dem Byte des<br />
Signals im Datensatz ist<br />
Wenn ja, Bytezähler weiterstellen<br />
Und nächstes Byte vergleichen<br />
R1 = = 0<br />
nein<br />
ja<br />
B #1<br />
B #0<br />
Wenn nein, in B die 0 für unidentisch schreiben<br />
Register<br />
r<strong>est</strong>aurieren<br />
Alle verwendeten Register r<strong>est</strong>aurieren<br />
ENDM<br />
Und ins Makro Compare zurück springen<br />
- 31 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Makro SENDAKKU<br />
Dieses Makro sendet den Inhalt des Akkus per<br />
V24 und wartet auf Grund der Trägheit des PCs<br />
Start<br />
Register<br />
sichern<br />
Alle verwendeten Register werden gesichert<br />
SBUF A<br />
Den Inhalt des Akkus in das Senderegister<br />
schreiben<br />
Ti = = 1<br />
ja<br />
Ti 0<br />
nein<br />
Ist die Sendung vorüber?<br />
Sendeflag wieder löschen<br />
R6 #20<br />
Ab hier ist <strong>eine</strong> Verzögerung von ca. 10ms<br />
eingebaut, wegen der Trägheit des PCs<br />
R5 #250<br />
R5 R5 - 1<br />
R5 = = 0<br />
ja<br />
R6 R6 - 1<br />
nein<br />
- 32 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
R6 = = 0<br />
nein<br />
ja<br />
Register<br />
r<strong>est</strong>aurieren<br />
ENDM<br />
- 33 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
; Programmname : Fernbed.a51<br />
; ProgFunktion : Auswerten der ankommenden Fernbedienungssignale und<br />
; Weiterleiten an den PC<br />
; Programmierer : Alexander Fuchs und <strong>David</strong> <strong>Zaadstra</strong><br />
; Datum : 26.2.2002 - 3.4.2002<br />
$ ERRORPRINT<br />
$ NOSYMBOLS<br />
$ NOMOD51<br />
$ NOLIST<br />
$ INCLUDE (reg515.INC)<br />
$ INCLUDE(macros.inc)<br />
$ LIST<br />
;--------------------------------------------------------------------------<br />
ORG 400BH<br />
jmp<br />
INT_TIMER0<br />
;------------------------------------------------------<br />
ORG 4100H<br />
; serielle Kommunikation vorbereiten<br />
InitV24<br />
; Stackpointer initialisieren<br />
mov SP,#80h<br />
; Interrupts initialisieren<br />
setb eal<br />
setb et0<br />
START:<br />
; wenn R7 != 0, dann Signal vorbei<br />
mov r7, #0<br />
; auf langes High warten (bevor das Signal beginnt)<br />
WAIT_HIGH<br />
; Timer 0 starten<br />
setb eal<br />
mov tmod, #1<br />
mov th0, #0ffh<br />
mov tl0, #9bh<br />
setb tr0<br />
; in R2 wird die Zahl der Impulse + Pausen abgelegt<br />
mov r2, #0<br />
; hier werden die Impulslängen gespeichert<br />
mov dptr, #0c000h<br />
; in R1 werden die High-Impulslängen gezählt<br />
; in R0 werden die Low-Impulslängen gezählt<br />
mov R1, #0<br />
- 34 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
LO:<br />
mov a, #0 ; a wird in der Timerroutine abgefragt<br />
; 0=LOW, alles andere=>HIGH<br />
mov R0, #0 ; R0 enthält die Anzahl der vergangenen 0,1ms<br />
mov b,r1 ; R1 enthält die Bitlänge des letzten<br />
; High-Impulses<br />
SAVE<br />
; Speichern des Wertes im externen Speicher<br />
jnb FERN, $ ; warten auf High und zählen der Länge <strong>durch</strong><br />
; Timer-Interrupt<br />
HI:<br />
mov a, #1 ; da<strong>mit</strong> Interrupt über High bescheid weiss<br />
mov R1, #0 ; R1 enthält die Anzahl der vergangenen 0,1ms<br />
mov b,r0 ; R0 enthält die Bitlänge des letzten<br />
; Low-Impulses<br />
SAVE<br />
; Speichern der Bitlänge im externen Speicher<br />
HI_GO:<br />
; Hier wird <strong>durch</strong> R7 überprüft, ob Signal zu Ende ist<br />
; wenn ja, jmp zu START<br />
push acc<br />
mov a, r7<br />
jz HI_GO2<br />
pop acc<br />
jmp START<br />
HI_GO2:<br />
pop<br />
jb<br />
acc<br />
FERN, HI_GO ; warten auf Low und zählen der Länge <strong>durch</strong><br />
; Timer-Interrupt<br />
jmp LO ; Nach jedem High folgt ein Low!<br />
;------------------------------------------------------<br />
; INTERRUPT-PROGRAMM<br />
INT_TIMER0:<br />
; Timer-Wert neu setzen<br />
mov TH0, #0FFh ; Werte sind so gewählt, dass Timer immer<br />
mov TL0, #9bh ; nach 0,1 ms in Interrupt geht<br />
; hier wird <strong>durch</strong> Akku er<strong>mit</strong>telt, ob High oder Low anliegt<br />
; liegt Low an, wird R0 hochgezählt<br />
; liegt High an, wird R1 hochgezählt<br />
jz TEMP<br />
jmp TEMP2<br />
TEMP: ljmp ADDLOW<br />
TEMP2:<br />
inc R1<br />
; Abfrage: falls Zeit größer 5ms => Signal zu Ende,<br />
; also verarbeiten und senden<br />
cjne R1, #50, TEMP3<br />
jmp TEMP4<br />
TEMP3: ljmp NOTEND<br />
TEMP4:<br />
- 35 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
clr eal ; Interrupts ausschalten<br />
push acc<br />
; sichern<br />
; Dieses Makro wandelt die 2 untersch. Impulslängen in Bits und Bytes<br />
; und schreibt diese an die alte Stelle an C000h<br />
POSTPROCESSDATA<br />
; jetzt wird der Datensatz eingelesen<br />
; an 1. Stelle steht die Anzahl der Signale<br />
mov dptr, #keys_data<br />
movx a, @dptr<br />
inc dptr<br />
; Dieses Makro vergleicht das empfangene Signal an C000<br />
; <strong>mit</strong> den Signalen im Datensatz<br />
COMPARE<br />
; das gefundene Signal steht im Akku<br />
SENDAKKU<br />
pop<br />
acc<br />
mov R7, #1 ; Signal zu Ende (R7 als Indikator)<br />
jmp NOTEND<br />
ADDLOW:<br />
inc R0<br />
NOTEND:<br />
clr tf0<br />
reti<br />
keys_data:<br />
$ INCLUDE(tasten.inc)<br />
end<br />
- 36 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
;--------------------------------------------------------------------------<br />
; Diese Makro-Datei enthält Makros, die für unsere Fernbedienungsabfrage<br />
; benutzt werden. Das Input-Signal wird auf FERN(P1.1) erwartet.<br />
;<br />
; Folgende Makros sind enthalten:<br />
;<br />
; Wait_High<br />
; Print<br />
; Save<br />
; Encode<br />
; Postprocessdata<br />
; Compare<br />
; Compareone<br />
; Sendakku<br />
;<br />
;--------------------------------------------------------------------------<br />
FERN EQU P1.1<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; W A I T _ H I G H<br />
; Dieses Makro wartet auf das High-Signal (ca 10ms), das vor bzw nach jedem<br />
; Signal der Fernbedienung kommt.<br />
;--------------------------------------------------------------------------<br />
WAIT_HIGH<br />
MACRO<br />
local reset,addition,notcarry<br />
push 00<br />
push 01<br />
; r0 und r1 repräsentieren <strong>eine</strong>n 16bit-Wert<br />
; r0 ist das lowbyte, r1 das highbyte<br />
reset:<br />
mov r0, #0<br />
mov r1, #0<br />
addition:<br />
; 16bit-Wert um eins erhöhen<br />
inc r0<br />
jnc notcarry<br />
inc r1<br />
notcarry:<br />
jnb FERN, reset ; wenn das Low-signal abbricht,<br />
; Reset und erneut warten<br />
cjne r1, #4, addition ; bei r1 = 5 (also ca 10ms) abbrechen,<br />
; ansonsten weiteraddieren<br />
jb FERN, $ ; auf das Header-Low warten<br />
pop 01<br />
pop 00<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 37 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; P R I N T<br />
; gibt die Codes der Impuls- und Pausenzeiten aus<br />
;--------------------------------------------------------------------------<br />
PRINT MACRO<br />
local m1<br />
m1:<br />
push acc<br />
mov r2, dpl<br />
mov dptr, #0c000h<br />
movx a, @dptr<br />
SENDADEZ<br />
mov a, #' '<br />
SENDA<br />
inc dptr<br />
djnz r2, m1<br />
ENDM<br />
mov<br />
SENDA<br />
mov<br />
SENDA<br />
mov<br />
SENDA<br />
pop<br />
a, #'E'<br />
a, #'N'<br />
a, #'D'<br />
acc<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 38 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; S A V E<br />
; speichert den Wert des b-Registers im externen Speicher. Anzahl der<br />
bisher<br />
; gespeicherten Werte werden in r2 gezählt<br />
;--------------------------------------------------------------------------<br />
SAVE MACRO<br />
push acc<br />
inc r2<br />
inc dptr<br />
mov a, b<br />
movx @dptr, a<br />
ENDM<br />
pop<br />
acc<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 39 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; E N C O D E<br />
; wird in Postprocessdata verwendet<br />
; wandelt ein Signalbyte von der Fernbedienung in ein Bit um<br />
; 1,4ms / 1,5ms / 1,6ms --> log. 1<br />
; 4ms / 5ms / 6ms --> log. 0<br />
; das Byte wird im Akku erwartet<br />
;--------------------------------------------------------------------------<br />
ENCODE MACRO<br />
local NOTHIGH1, NOTHIGH2, NOTHIGH3, NOTLOW1, NOTLOW2, NOTLOW3, HI,<br />
LO, ENDMACRO<br />
usw.<br />
; <strong>eine</strong> einfache Vergleichsroutine:<br />
; trifft die erste Bedingung nich zu, wird zur Nächsten gesprungen<br />
cjne a, #13, NOTHIGH4<br />
jmp HI<br />
NOTHIGH4:<br />
cjne a, #14, NOTHIGH1<br />
jmp HI<br />
NOTHIGH1:<br />
cjne a, #15, NOTHIGH2<br />
jmp HI<br />
NOTHIGH2:<br />
cjne a, #16, NOTHIGH3<br />
jmp HI<br />
NOTHIGH3:<br />
cjne a, #17, NOTHIGH5<br />
jmp HI<br />
NOTHIGH5:<br />
cjne a, #3, NOTLOW4<br />
jmp LO<br />
NOTLOW4:<br />
cjne a, #4, NOTLOW1<br />
jmp LO<br />
NOTLOW1:<br />
cjne a, #5, NOTLOW2<br />
jmp LO<br />
NOTLOW2:<br />
cjne a, #6, NOTLOW3<br />
jmp LO<br />
NOTLOW3:<br />
cjne a, #7, NOTLOW5<br />
jmp LO<br />
NOTLOW5:<br />
HI:<br />
setb c<br />
jmp ENDMACRO<br />
LO:<br />
clr c<br />
ENDMACRO:<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 40 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; P O S T P R O C E S S D A T A<br />
; wandelt <strong>mit</strong> Hilfe von Encode die gespeicherten Impulslängen in Bits und<br />
Bytes<br />
; und schreibt diese an die alte Stelle an C000h<br />
;--------------------------------------------------------------------------<br />
POSTPROCESSDATA MACRO<br />
local NOT_ALL_READ, BYTE_NOT_FULL, temp, temp2, notcarry,<br />
bitset, bitnotset, NULL_FILL<br />
push 00<br />
push 01<br />
push 07<br />
mov r0, #0 ; Anzahl der Impulse/Pausen<br />
mov r1, #8 ; <strong>mit</strong> R1 wird immer ein Byte runtergezählt<br />
mov r7, #0 ; dpl-Zeiger<br />
; die ersten 4 Bytes überspringen, da diese immer gleich sind<br />
mov dptr, #0c004h<br />
; entsprechend auch die Anzahl der gelesenen Bytes um 4 verringern<br />
push acc<br />
mov a, r2<br />
subb a, #4<br />
mov r2, a<br />
pop acc<br />
NOT_ALL_READ:<br />
inc<br />
r0<br />
; Byte aus dem Speicher holen und in Bit wandeln<br />
push acc<br />
movx a, @dptr<br />
ENCODE<br />
; setzt P5.1, wenn Byte log.1 darstellt<br />
pop acc<br />
; Bit in den Akku shiften<br />
rlc a<br />
; <strong>eine</strong> Speicherstelle weiterrücken<br />
inc dptr<br />
notcarry:<br />
; prüfen ob ein Byte voll ist (nach 8 geshifteten Bits)<br />
djnz r1, BYTE_NOT_FULL<br />
; Byte voll, also schreiben<br />
push dpl<br />
mov dpl, r7<br />
movx @dptr, a<br />
- 41 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
inc<br />
pop<br />
r7<br />
dpl<br />
mov r1, #8 ; Bytezähler zurücksetzen<br />
BYTE_NOT_FULL:<br />
; wenn noch nicht alle Bytes gelesen und gewandelt sind,<br />
; wieder nach oben springen<br />
djnz r2, temp<br />
jmp temp2<br />
temp:<br />
ljmp NOT_ALL_READ<br />
temp2:<br />
mov<br />
a, r0<br />
; Anzahl der Bytes = gelesene Bytes / 8, weil jedes Byte nur noch ein<br />
Bit ist<br />
rr a<br />
rr a<br />
rr a<br />
; <strong>mit</strong> Nullen auffüllen (für die kürzeren Codes)<br />
push acc<br />
mov dph,#0c0h<br />
mov dpl, r7<br />
mov r7, #4<br />
NULL_FILL:<br />
mov a, #0<br />
movx @dptr, a<br />
inc dptr<br />
djnz r7, NULL_FILL<br />
pop acc<br />
ENDM<br />
pop 07<br />
pop 01<br />
pop 00<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 42 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; C O M P A R E<br />
; vergleicht <strong>mit</strong> Hilfe von Compareone die Datensätze, die bei dptr<br />
anfangen, <strong>mit</strong><br />
; dem Signal bei C000 und gibt die Nummer des gefundenen Signals im Akku<br />
zurück<br />
; die Gesamtanzahl der Signale im Datensatz wird im Akku erwartet<br />
;--------------------------------------------------------------------------<br />
COMPARE MACRO<br />
local nextsignal<br />
push 00<br />
push 01<br />
push 02<br />
push 03<br />
push b<br />
push dph<br />
push dpl<br />
mov r0, #0 ; R0 ist der Index des Signals/Datensatzes<br />
mov r2, a ; R2 = Anzahl der Signale im Datensatz<br />
nextsignal:<br />
; gibt bei Übereinstimmung der Signale b = 1, ansonsten b = 0 zurück<br />
COMPAREONE<br />
mov r3, b<br />
cjne r3, #0, signalfound<br />
inc r0 ; nächstes Signal vergleichen<br />
; den dptr um 6 erhöhen (nächstes Signal)<br />
inc dptr<br />
inc dptr<br />
inc dptr<br />
inc dptr<br />
inc dptr<br />
inc dptr<br />
djnz r2, nextsignal<br />
signalfound:<br />
; wenn kein Signal gefunden wurde, wird 27d zurückgegeben<br />
mov a, r0<br />
pop dpl<br />
pop dph<br />
pop b<br />
pop 03<br />
pop 02<br />
pop 01<br />
pop 00<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 43 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
; C O M P A R E O N E<br />
; wird in Compare verwendet<br />
; vergleicht immer nur 1 Signal (6 Bytes) des Datensatzes <strong>mit</strong> dem<br />
empfangenen<br />
; Signal<br />
; hinterlässt bei Übereinstimmung des Signals b = 1<br />
; ansonsten b = 0<br />
;--------------------------------------------------------------------------<br />
COMPAREONE MACRO<br />
local m1, m2, signalfalse<br />
push 00<br />
push 01<br />
push 02<br />
push acc<br />
push dph<br />
push dpl<br />
; sichern<br />
mov r1, #6 ; Anzahl der Bytes pro Signal<br />
m2:<br />
mov r2, #0h ; dpl-Zeiger<br />
movx a,@dptr ; Datensatzbyte holen, dptr ist von<br />
; Compare g<strong>est</strong>ellt<br />
mov r0,a ; zum Vergleichen in R0 schieben<br />
inc dptr ; auf nächstes Byte stellen<br />
push dph<br />
push dpl<br />
mov dph,#0C0h ; empfangenes Byte holen<br />
mov dpl, r2<br />
inc r2<br />
movx a,@dptr<br />
pop<br />
pop<br />
dpl<br />
dph<br />
subb a,r0<br />
jz m1<br />
; vergleichen<br />
m1:<br />
sind<br />
mov b, #0 ; Signale nicht identisch<br />
jmp signalfalse<br />
djnz r1, m2<br />
; so lange, bis 6 Bytes <strong>eine</strong>s Signals verglichen<br />
mov b, #1 ; Signal identisch<br />
signalfalse:<br />
pop dpl<br />
pop dph<br />
pop acc<br />
pop 02<br />
pop 01<br />
pop 00<br />
- 44 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
$EJECT<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
;S E N D A K K U<br />
;sendet den Inhalt des Akkus per V24.<br />
;--------------------------------------------------------------------------<br />
SENDAKKU MACRO<br />
local m1<br />
push 05<br />
push 06<br />
mov sbuf,a ; Zeichen an serielle Schnittstelle<br />
jnb ti,$ ; warten bis Sendung fertig ist<br />
clr ti ; Sendeflag zurücksetzen<br />
; ca 10ms warten, wegen Trägheit des Terminals<br />
mov<br />
r6,#20<br />
m1: mov r5,#250<br />
djnz r5,$<br />
djnz r6,m1<br />
pop 06<br />
pop 05<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
;//////////////////////////////////////////////////////////////////////////<br />
;--------------------------------------------------------------------------<br />
;I N I T V 2 4<br />
;initialisiert die Schnittstelle auf 9600 Baud, 1 Stoppbit und<br />
;k<strong>eine</strong> Parity<br />
;--------------------------------------------------------------------------<br />
InitV24 MACRO<br />
setb bd ;Baudratengenerator (ADCON)<br />
orl pcon,#80h ;Baudrate auf 9600<br />
mov scon,#50H ;Mode 1 wählen+REN<br />
ENDM<br />
;//////////////////////////////////////////////////////////////////////////<br />
- 45 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
6. Kommunikation Mikrokontroller -> PC<br />
6.1 Empfang der Daten <strong>mit</strong> den Communications-Funktionen der Win32-API<br />
Das Win32 API der Microsoft Windows Betriebssysteme stellt unter dem Bereich<br />
„Communications“ Funktionen zur Verfügung, die es ermöglichen, Daten über die seriellen<br />
Schnittstellen auszutauschen.<br />
Die Kommunikation zu <strong>eine</strong>r der seriellen Schnittstellen wird über die Funktion<br />
CreateFile aufgebaut. Die Schnittstelle wird also wie <strong>eine</strong> Datei angesprochen.<br />
Der Unterschied liegt nur in den Parametern, die bei der Initialisierung übergeben werden.<br />
Will man zum Beispiel COM1 ansprechen, sieht die Initialisierung so aus:<br />
CODE:<br />
DCB dcb;<br />
HANDLE com;<br />
DWORD error;<br />
BOOL success;<br />
com = CreateFile(<br />
"COM1",<br />
// Kontrollstruktur für die serielle Schnittstelle<br />
// Handle der Schnittstelle<br />
// für zurückgegebene Fehlercodes<br />
// COM1 öffnen<br />
GENERIC_READ |<br />
GENERIC_WRITE,<br />
// lesen und schreiben<br />
0, // exklusiver Zugriff<br />
NULL,<br />
OPEN_EXISTING,<br />
// k<strong>eine</strong> Sicherheitsattribute<br />
// muss bei der seriellen Schnittstelle<br />
// verwendet werden<br />
0, // kein asynchroner Zugriff<br />
);<br />
NULL<br />
// muss bei der seriellen Schnittstelle<br />
// verwendet werden<br />
// Ist das zurückgegebene Handle gültig?<br />
if (com == INVALID_HANDLE_VALUE)<br />
{<br />
error = GetLastError(); //Fehlercode holen<br />
// hier Fehlerbehandlung einfügen<br />
}<br />
- 46 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
success = GetCommState(com, &dcb);<br />
// Ist der Aufruf fehlgeschlagen?<br />
if (!success)<br />
{<br />
// hier Fehlerbehandlung einfügen<br />
}<br />
/* Nun setzen wir die wichtigsten Parameter der Kontrollstruktur. Die Baudrate setzen wir auf<br />
9600, die Bytegröße auf 8, ein Stoppbit, kein Parity. Die übrigen Werte sind schon gesetzt, denn<br />
wir haben sie ja <strong>mit</strong> GetCommState() geholt. */<br />
dcb.BaudRate = 9600;<br />
dcb.ByteSize = 8;<br />
dcb.Parity = NOPARITY;<br />
dcb.StopBits = ONESTOPBIT;<br />
success = SetCommState(com, &dcb);<br />
// Ist der Aufruf fehlgeschlagen?<br />
if (!success)<br />
{<br />
// hier Fehlerbehandlung einfügen<br />
}<br />
Ein Byte zu lesen ist nun ganz einfach:<br />
CODE:<br />
char byte;<br />
int bytesread;<br />
ReadFile(<br />
com, // Handle<br />
&byte, // Zeiger auf den Speicher des Bytes, das gelesen wird<br />
1, // Anzahl der zu lesenden Bytes<br />
&bytesread, // Anzahl der gelesenen Bytes (wird von der Funktion gesetzt)<br />
NULL // für unsere Zwecke unwichtig<br />
);<br />
// Wenn nun in bytesread <strong>eine</strong> 1 steht, haben wir in der Variable byte unser gelesenes<br />
// Byte. Ansonsten sind noch k<strong>eine</strong> Daten an der Schnittstelle angekommen.<br />
Jedoch ist nach der Initialisierung (bevor wir wie oben die Bytes lesen) noch ein weiterer<br />
Schritt erforderlich, da<strong>mit</strong> die Abfrage der Schnittstelle in Echtzeit ablaufen kann.<br />
- 47 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Das Problem ist, dass es standardmäßig beim Lesen und Schreiben auf die Schnittstelle<br />
k<strong>eine</strong>n Timeout gibt. D.h. wenn wir ein Byte lesen wollen, aber k<strong>eine</strong>s vorhanden ist, bleiben<br />
wir in der ReadFile-Funktion hängen bis ein Byte kommt. Dies ist natürlich nicht erwünscht.<br />
Deshalb setzen wir den Timeout so, dass ein Byte zurückgeliefert wird, sofern es vorhanden<br />
ist. Ansonsten soll es direkt weitergehen <strong>mit</strong> dem Programm.<br />
Der Timeout wird folgendermaßen verändert:<br />
CODE:<br />
COMMTIMEOUTS timeout;<br />
if(!GetCommTimeouts(com, &timeout))<br />
{<br />
// hier Fehlerbehandlung einfügen<br />
}<br />
timeout.ReadTotalTimeoutConstant = 1;<br />
timeout.ReadTotalTimeoutMultiplier = 0;<br />
timeout.ReadIntervalTimeout = 1;<br />
if(!SetCommTimeouts(com, &timeout))<br />
{<br />
// hier Fehlerbehandlung einfügen<br />
}<br />
Der ReadIntervalTimeout gibt an, wie viel Zeit zwischen der Ankunft von 2 Bytes<br />
verstreichen darf, die ReadTotalTimeoutConstant gibt an, wie lange der Lesevorgang<br />
insgesamt dauern darf.<br />
Optimal wäre natürlich ein Wert von 0. Dieser ist jedoch reserviert und bedeutet, dass es<br />
k<strong>eine</strong>n Timeout gibt. Deshalb werden beide Werte auf 1 gesetzt. Da<strong>mit</strong> hätten wir aber immer<br />
noch 1ms Wartezeit, was zwar nicht sehr viel ist, den Programmfluss aber dennoch<br />
verlangsamt. Deshalb wird der ReadTotalTimeoutMultiplier auf 0 gesetzt. Wie am Namen<br />
unschwer zu erkennen ist, stellt diese Variable <strong>eine</strong>n Vervielfacher dar. So<strong>mit</strong> werden die<br />
Timeout-Werte <strong>mit</strong> 0 multipliziert, wo<strong>durch</strong> wir das gewünschte Verhalten erzielt haben: Ist<br />
ein Zeichen da, lesen wir es, ansonsten wird die Funktion direkt verlassen.<br />
- 48 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
6.2 Die MTV24-Klasse<br />
Diese Klasse wurde geschrieben, um die V24-Kommunikation sauber in das objektorientierte<br />
Konzept unseres Codes einzufügen. Die MTV24-Klasse selbst stellt nur ein Interface dar.<br />
Bisher ist nur das Lesen vorgesehen, da wir für unser Projekt nicht mehr als das brauchten,<br />
jedoch könnte sie auch leicht erweitert werden.<br />
Davon abgeleitet ist die MTV24Windows-Klasse, die die Implementation für unsere<br />
Plattform, nämlich Windows, bereitstellt.<br />
Da der Windows-Code zum Initialisieren und Lesen der seriellen Schnittstelle bereits im<br />
vorigen Abschnitt behandelt wurde, gehen wir hier nicht noch einmal darauf ein.<br />
Den Code zu beiden Klassen finden Sie im Verzeichnis Communications.<br />
- 49 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7. Die ManiaTech <strong>3D</strong>-Engine<br />
Die ManiaTech <strong>3D</strong>-Engine ist <strong>eine</strong> Eigenentwicklung, die ich (<strong>David</strong> <strong>Zaadstra</strong>) schon vor der<br />
Projektarbeit in Angriff genommen habe. Sie soll das Arbeiten <strong>mit</strong> <strong>3D</strong>-Grafik erleichtern.<br />
Nur geringe Teile der Engine wurden während der Projektarbeit verändert. Da sie ausserdem<br />
fast gar nicht dokumentiert ist und ich sie evtl. später einmal zu kommerziellen Zwecken<br />
einsetzen möchte, habe ich mich dagegen entschieden, den Quellcode zu veröffentlichen. Auf<br />
der Projekt-CD sind nur die Libraries zu finden.<br />
Um Sie jedoch nicht ganz im Unklaren zu lassen, finden Sie nachfolgend <strong>eine</strong> Einführung in<br />
die <strong>3D</strong>-Grafik und <strong>eine</strong>n kurzen Überblick über die Struktur und Arbeitsweise der Engine.<br />
7.1 Einführung in (Echtzeit-)<strong>3D</strong>-Grafik<br />
7.1.1 Polygone<br />
Wenn man heutzutage von <strong>3D</strong>-Grafik spricht, meint man normalerweise Polygongrafik.<br />
Andere Methoden zur Darstellung von dreidimensionalen Objekten, wie z.B. die<br />
Voxelgrafik 2 , werden nur noch selten genutzt. Aktuelle Grafikkarten sind auf die Darstellung<br />
von Polygongrafiken optimiert. Der Begriff Polygongrafik ist an sich aber auch nicht ganz<br />
richtig, denn poly = viel und gon = Eck, also Vieleckgrafik. Praktisch werden jedoch nur<br />
Dreiecke 3 von der Hardware verarbeitet (Im folgenden werden wir deshalb den Begriff<br />
Polygongrafik nur in Bezug auf Dreiecke benutzen.). Ein Dreieck hat den Vorteil, dass es die<br />
simpelste geometrische Form ist, die auch <strong>eine</strong> Fläche hat. Simpel heisst, dass Dreiecke nicht<br />
komplex werden können und zudem immer alle Eckpunkte auf <strong>eine</strong>r Ebene liegen. Dies ist<br />
bei Vier- oder Mehrecken nicht immer der Fall.<br />
Bei Vierecken beispielsweise entstehen drei Richtungsvektoren (der vierte ist abhängig von 2<br />
anderen), die linear unabhängig sein können. Ist dies der Fall, so erhalten wir <strong>eine</strong>n Raum,<br />
k<strong>eine</strong> Ebene. Dies ist unerwünscht, da so<strong>mit</strong> das Zeichnen nicht so einfach zu machen ist.<br />
In anderen ungünstigen Fällen können die Eckpunkte <strong>eine</strong>s Vierecks über Kreuz liegen oder<br />
<strong>eine</strong> konkave Form bilden. Auch diese Fälle sind zum Zeichnen der Grafik zu kompliziert.<br />
Zur Veranschaulichung folgende Beispiele:<br />
2 Voxel ist die Abkürzung für Volume Element (abgeleitet von Pixel = Picture Element). Ein Voxel stellt nichts<br />
weiter als <strong>eine</strong>n Punkt im Raum dar, der <strong>eine</strong> Farbe haben kann, so wie ein Pixel auf <strong>eine</strong>r Ebene. Möchte man<br />
hochaufgelöste Objekte aus Voxeln haben, entsteht ein immenser Speicherbedarf. Vereinfachte Voxelgrafiken<br />
wurden schon zur Darstellung von Heightmaps verwendet, z.B. in Outcast[6] und Comanche [7]<br />
3 APIs wie OpenGL erlauben auch andere geometrische Formen, hierzu sei aber auf weiterführende Literatur<br />
verwiesen[5]<br />
- 50 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Solche Formen ergeben zusammengenommen viele Spezialfälle. Ein Dreieck lässt sich<br />
dagegen sehr leicht zeichnen, indem man für jede Zeile, die sich auf dem Bildschirm ergibt,<br />
den Anfangs- und Endpunkt findet und diesen Bereich zeichnet. Diesen Algorithmus<br />
bezeichnet man als „Scanline Rasterization“. Nähere Informationen dazu finden Sie unter [1].<br />
Ein Dreieck stellt also die b<strong>est</strong>e geometrische Form zur Darstellung von dreidimensionalen<br />
Objekten dar, da es k<strong>eine</strong> simplere Möglichkeit gibt, <strong>eine</strong> Fläche darzustellen, und sich<br />
dennoch alle erdenklichen Gebilde aus Dreiecken zusammensetzen lassen.<br />
7.1.2 Darstellung von Dreiecken:<br />
In aktueller Hardware werden die Polygone <strong>durch</strong> Vertizes und Indizes repräsentiert. Ein<br />
Vertex ist nichts anderes als ein Ortsvektor (<strong>mit</strong> Zusatzinformationen, doch dazu später<br />
mehr.). Die Indizes dienen dazu, die Vertizes anzugeben, die ein Dreieck bilden.<br />
DirectX (auch OpenGL) bietet hierfür Vertex- und Indexbuffer. Haben wir z.B. <strong>eine</strong>n<br />
Vertexbuffer (der ebenso wie der Indexbuffer nichts anderes als ein Array ist), der drei<br />
Ortsvektoren des dreidimensionalen Raumes enthält, dann können wir <strong>mit</strong> <strong>eine</strong>m Indexbuffer,<br />
der drei Ganzzahlvariablen enthält, ein Dreieck bilden. Im Pseudocode sähe das in etwa so<br />
aus:<br />
Vertexbuffer vb[3]<br />
// hier den Vertexbuffer <strong>mit</strong> Ortsvektoren füllen<br />
Indexbuffer ib[3]<br />
ib[0] = 0<br />
ib[1] = 1<br />
ib[2] = 2<br />
Anschließend ruft man <strong>eine</strong> Routine zum Zeichnen auf, der man die beiden Arrays übergibt.<br />
Diese verweist dann <strong>mit</strong> den ersten 3 Indexbuffer-Einträgen auf die entsprechenden Einträge<br />
im Vertexbuffer, sodass aus den 3 Vertizes ein Dreieck entsteht.<br />
Einschub:<br />
- 51 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
In diesem Fall ist es natürlich nicht sehr sinnvoll, <strong>eine</strong>n Indexbuffer zu benutzen, da die<br />
Grafikkarte die drei Vektoren ja auch ohne Indexbuffer einfach einlesen könnte (dies lässt sich<br />
auch so machen.). Jedoch kommt es normalerweise nicht vor, dass man einzelne Dreiecke<br />
zeichnet; in der Praxis grenzen meist mehrere Dreiecke aneinander, sodass Vertizes mehrfach<br />
<strong>durch</strong> die Indizes referenziert werden. Dies spart erstens Platz, da ein Index weniger Speicher<br />
braucht als ein Vertex, zweitens ermöglicht das Indizieren der Grafikkarte, Cache-<br />
Optimierungen <strong>durch</strong>zuführen, hierzu sei jedoch wieder auf weiterführende Literatur<br />
verwiesen.<br />
7.1.3 Transformationen:<br />
Die Positionen der Dreiecke im dreidimensionalen Raum sind nun f<strong>est</strong>gelegt. Es fehlt<br />
allerdings noch ein Schritt, um die Vertizes auf den zweidimensionalen Bildschirm zu<br />
projizieren. Hierzu verwenden wir <strong>eine</strong> Matrix, <strong>mit</strong> der jeder Vertex multipliziert wird. Diese<br />
Matrix nennt sich Projektionsmatrix. Eine Projektionsmatrix sieht folgendermaßen aus:<br />
wobei<br />
und<br />
V bezeichnet den Viewport. Das ist der Bildausschnitt, der gezeichnet wird. V W bezeichnet<br />
die Breite, V H die Höhe des Bereichs. Normalerweise ist dies die Bildschirmauflösung bzw.<br />
die Größe des Fensters, z.B. V W = 640 und V H = 480.<br />
Z n und Z f geben die Entfernung der sogenannten „clipping planes“ an. Das sind die Ebenen,<br />
an denen der Sichtbereich endet. Diese Ebenen liegen orthogonal zum Sichtvektor des<br />
- 52 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Betrachters. Z n ist die „near” bzw. „ front clipping plane “, Z f die „far” bzw. „ back clipping<br />
plane “. Der Sichtbereich wird auch als „viewing frustum“ bezeichnet. Ein solches „frustum“<br />
sieht so aus:<br />
Nach der Projektionstransformation befinden sich die Koordinaten der Vertizes im Bereich<br />
von (-1,-1,0) bis (1,1,1). Liegen sie nicht in diesem Bereich, werden sie nicht gezeichnet,<br />
denn sie sind ja nicht sichtbar.<br />
In der Praxis werden auch noch andere Transformationen verwendet. Dies sind die „world<br />
transform“ und die „view transform“. Die world transform wird verwendet, um ein Objekt<br />
(<strong>eine</strong> Ansammlung zusammengehörender Vertizes) zu transformieren. Hat man z.B. ein <strong>3D</strong>-<br />
Modell <strong>eine</strong>s Menschen, möchte man dieses unabhängig vom R<strong>est</strong> der Welt bewegen. So<strong>mit</strong><br />
hat man normalerweise für jedes Objekt <strong>eine</strong> „world matrix“.<br />
Die „view matrix“ ist in der Regel nur einmal vorhanden. Sie repräsentiert die „Kamera“, also<br />
die Position des Betrachters. Bewegt sich der Betrachter beispielsweise 5 Einheiten in z-<br />
Richtung, muss die view matrix die Vertizes um 5 Einheiten in die entgegengesetzte Richtung<br />
bewegen.<br />
Die drei Transformationen werden in folgender Reihenfolge ausgeführt:<br />
- world transform<br />
- view transform<br />
- projection transform<br />
Um den Rechenaufwand gering zu halten, werden üblicherweise zuerst die Matrizen<br />
<strong>mit</strong>einander und anschließend die resultierende Matrix <strong>mit</strong> den Vertizes multipliziert.<br />
Weiterführende Informationen zu Transformationen gibt es z.B. unter [2] und [3].<br />
- 53 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7.1.4 Texturen und Farben:<br />
Nun wissen wir, wie wir die Position <strong>eine</strong>s Dreieckes (bzw. s<strong>eine</strong>r Eckpunkte) b<strong>est</strong>immen,<br />
doch diese Dreiecke haben noch k<strong>eine</strong> Farbe.<br />
Zum „Färben“ der Dreiecke gibt es zwei Möglichkeiten:<br />
1. Vertexfarben:<br />
Zu den oben erwähnten Zusatzinformationen in <strong>eine</strong>m Vertex gehört unter anderem<br />
auch <strong>eine</strong> Farbe. Diese Farbe ist <strong>eine</strong> 32Bit-Ganzzahlvariable, die jeweils 8 Bit für die<br />
Rot-, Grün-, Blau- und Alphakomponente enthält. Alpha bedeutet Transparenz und<br />
ermöglicht, (teilweise) transparente Dreiecke zu zeichnen.<br />
Beim Zeichnen <strong>eine</strong>s Dreiecks werden die Vertexfarben interpoliert.<br />
2. Texturen:<br />
Eine Textur stellt ein 1- bis 3-dimensionales Feld von Farbwerten dar. In der Praxis<br />
werden fast nur 2D-Texturen, also „Bilder“, verwendet. Ein Vertex hat hierzu<br />
Koordinaten gespeichert (von derselben Dimension wie die<br />
Textur), die b<strong>est</strong>immen, welcher Teil des Bildes auf dem Vertex liegt.<br />
Dementsprechend liegen auf dem R<strong>est</strong> des Dreiecks die dazwischenliegenden Pixel.<br />
Hierzu <strong>eine</strong> Illustration:<br />
- 54 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7.1.5 Licht:<br />
Eine weitere wichtige Komponente in der <strong>3D</strong>-Grafik sind Lichtquellen.<br />
Es gibt verschiedene Sorten von Lichtquellen, der Einfachheit halber beschränken wir uns<br />
jedoch auf die simpelste, das sogenannte „directional light“. Eine solche Lichtquelle hat nur<br />
<strong>eine</strong> Richtung und <strong>eine</strong> Farbe. Die Position des Lichtes wird ignoriert. Der Lichteinfall wird<br />
so berechnet, als wäre das Licht unendlich weit entfernt und die Strahlen da<strong>mit</strong> parallel.<br />
Eine Lichtquelle wirkt nun je nach Beleuchtungsmodell („lighting model“) unterschiedlich<br />
auf die Farbe <strong>eine</strong>s Dreiecks (bzw. <strong>eine</strong>s Pixels des Dreiecks) ein. Es gibt zwei<br />
Beleuchtungsmodelle, die direkt von aktueller Hardware unterstützt werden.<br />
1. Flat Shading:<br />
Nehmen wir an, unser Dreieck ist gegeben <strong>durch</strong> die drei Ortsvektoren von drei<br />
Vertizes a, b und c. Dann ist die Normale des Dreiecks das Vektorprodukt von (ba)x(c-a).<br />
Die Lichtintensität wird b<strong>est</strong>immt <strong>durch</strong> das Skalarprodukt dieser Normale<br />
<strong>mit</strong> dem Richtungsvektor der Lichtquelle. Die Farbwerte der Pixel des Dreiecks<br />
werden nun <strong>mit</strong> dem Skalarprodukt multipliziert. Wenn Lichtvektor und Normale<br />
übereinstimmen, ist das Skalarprodukt 1 und da<strong>mit</strong> die volle Lichtintensität gegeben.<br />
Sind die Vektoren >= 90° auseinander, ist die Lichtintensität
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7.1.6 Zeichnen („Rendern“) von Grafik:<br />
Das eigentliche Rendern der Grafik wird meist vollständig von der Grafikkarte übernommen.<br />
Der Programmierer tut in der Regel folgendes:<br />
- Er legt die Vertex- und Indexbuffer an und füllt diese <strong>mit</strong> s<strong>eine</strong>n Daten.<br />
- Er lädt Texturen und weist die API an, diese beim Rendern zu verwenden.<br />
- Er setzt Transformationsmatrizen.<br />
Den R<strong>est</strong> erledigt dann die Grafikkarte. Sie transformiert die Vertizes basierend auf den<br />
gegebenen Matrizen. Ergebnis ist die Pixelposition der Vertizes auf dem Bildschirm. Der<br />
Bildschirmbereich wird anschließend <strong>mit</strong> den Farbwerten gefüllt, die sich <strong>durch</strong> Vertexfarben,<br />
Texturen und Lichteinfall ergeben.<br />
Die ganze Bandbreite der Echtzeit-<strong>3D</strong>-Grafik lässt sich auf so wenigen Seiten nicht<br />
ver<strong>mit</strong>teln, dazu ist ein ganzes Buch nötig. Glücklicherweise wurden solche Bücher schon<br />
von anderen Leute verfasst. Einen sehr guten Überblick über den Bereich der Echtzeit-<strong>3D</strong>-<br />
Grafik, des „Realtime Rendering“, bietet das gleichnamige Buch [4].<br />
- 56 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7.2 Struktur der Engine<br />
Die ManiaTech <strong>3D</strong>-Engine ist stark objektorientiert. Sie ist so konzipiert, dass sie gut<br />
erweiterbar und portierbar ist. Zu diesem Zweck gibt es verschiedene Interface- und<br />
Basisklassen, von denen man ableiten kann, um neue Funktionalität hinzuzufügen, ohne<br />
b<strong>est</strong>ehenden Code ändern zu müssen. Ein Diagramm zur Veranschaulichung:<br />
Dieses Diagramm enthält nur <strong>eine</strong>n Teil der Klassen, aus denen die Engine b<strong>est</strong>eht. Diese<br />
Klassen sind jedoch die wesentlichsten, die zur Realisierung des Projektes notwendig waren<br />
und reichen aus, um den Aufbau der Engine grob zu verdeutlichen.<br />
In der Basis der Engine gibt es grundlegende Klassen wie MTShared oder MTManager. Die<br />
MTShared-Klasse beispielsweise sorgt für das „Reference Counting“ 1 , die MTManager-<br />
Klasse implementiert <strong>eine</strong> Singleton-Klasse 2 als Template.<br />
1 Die Anzahl der Referenzen auf das Objekt wird gezählt. Benötigt <strong>eine</strong> andere Klasse das Objekt, erhöht es den<br />
Zähler <strong>mit</strong> AddRef(), will sie das Objekt freigeben, so dekrementiert sie den Zähler <strong>mit</strong> Release(). Sinkt der<br />
Zähler auf 0, so löscht sich das Objekt selbst.<br />
2 Ein Singleton ist <strong>eine</strong> Klasse, die nur <strong>eine</strong> Instanz besitzt. Über die static-Methode GetInstance() bekommt man<br />
den Pointer auf diese Instanz.<br />
- 57 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
Andere Klassen wie MTRenderContext, MTVertexBuffer, etc. stellen Interfaceklassen dar,<br />
die k<strong>eine</strong> (oder nur wenig) Implementation besitzen. Die Implementation erfolgt dann <strong>durch</strong><br />
die abgeleiteten Klassen MTRenderContextDirect<strong>3D</strong>8, etc. Dies hat den Vorteil, dass sich<br />
verschiedene Implementationen benutzen lassen, ohne dass der Code des Benutzers geändert<br />
werden muss.<br />
Einschub:<br />
Solche Prinzipien verwenden auch kommerzielle Programme wie z.B. WinAmp. Dort<br />
werden DLLs für verschiedene Musikformate in <strong>eine</strong>m eigenen Verzeichnis<br />
gespeichert. Das Programm sucht dann nach <strong>eine</strong>r DLL für das Dateiformat, das der<br />
Benutzer abspielen möchte. Diese DLL implementiert die Funktion(en) oder Klasse(n)<br />
zum Abspielen der Sounddatei.<br />
Klassen wie MTVertexBuffer sind von MTRenderContext abhängig und müssen daher auch<br />
von dieser Klasse erstellt werden. Ruft der Benutzer also die entsprechende Methode zum<br />
Erstellen <strong>eine</strong>s VertexBuffers auf, erfolgt das Erstellen <strong>durch</strong> die implementierende Klasse, in<br />
diesem Fall MTRenderContextDirect<strong>3D</strong>8. Sie erstellt <strong>eine</strong> Instanz von<br />
MTVertexBufferDirect<strong>3D</strong>8, gibt aber <strong>eine</strong>n Pointer auf MTVertexBuffer zurück, sodass die<br />
Implementation verborgen bleibt.<br />
- 58 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
7.3 Arbeitsweise der Engine<br />
Die wichtigsten Klassen zum Rendern von Grafik sind der MTRenderManager, der<br />
MTRenderContext sowie MTRenderable.<br />
Der MTRenderManager verwaltet verschiedene MTRenderContexts. Dazu benötigt er nur<br />
<strong>eine</strong>n Pointer auf die Funktion, die <strong>eine</strong>n solchen erstellt. Diese Funktion kann z.B aus <strong>eine</strong>r<br />
DLL geladen werden, sodass die Implementation des MTRenderContexts jederzeit<br />
ausgetauscht werden kann, ohne dass das Programm geändert wird.<br />
Der MTRenderContext ist nun für das Rendern von Grafik zuständig. Hierzu hat er <strong>eine</strong> Liste<br />
von MTRenderable-Objekten. Ein MTRenderable ist nur ein Interface, das <strong>eine</strong> Funktion zum<br />
Rendern zur Verfügung stellt. So ist es möglich, jederzeit neue Klassen von MTRenderable<br />
abzuleiten und rendern zu lassen, ohne die MTRenderContext-Klasse zu verändern.<br />
Das gesamte Rendern der Engine geschieht also <strong>durch</strong> <strong>eine</strong>n Aufruf der Render-Funktion von<br />
MTRenderManager. Dieser wiederum ruft die Render-Funktion aller MTRenderContexts auf,<br />
die wiederum alle MTRenderable-Objekte rendern.<br />
- 59 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8. Die <strong>Landschaft</strong><br />
Die <strong>Landschaft</strong> liegt als ausführbare Windows-Datei vor, die <strong>mit</strong> Microsoft Visual C++ 6<br />
entwickelt wurde. Sie b<strong>est</strong>eht aus <strong>eine</strong>m Optionsmenü, das <strong>mit</strong> Hilfe des MFC-<br />
Klassenassistenten von Visual C++ erstellt wurde.<br />
Die beiden Hauptaufgaben des Programms sind das Zeichnen der <strong>3D</strong>-Grafik sowie der<br />
Empfang der Bytes von der V24-Schnittstelle und das Ausführen der entsprechenden<br />
Aktionen.<br />
Der Benutzer hat die Möglichkeit, sich als Betrachter entweder laufend oder fliegend in der<br />
<strong>Landschaft</strong> zu bewegen. Weiterhin steht ein Demomodus zur Verfügung, in dem sich der<br />
Betrachter auf <strong>eine</strong>r zufallsbasierten Flugbahn bewegt.<br />
Da der C++-Code relativ umfangreich ist, wird nur der Code des Hauptprogramms der<br />
Dokumentation beigefügt. Die Funktion der anderen Programmteile wird nur beschrieben,<br />
ohne auf die technischen Details einzugehen; der interessierte Leser kann sich den Code auf<br />
der zugehörigen CD-ROM anschauen.<br />
8.1 Das Optionsmenü<br />
Das Optionsmenü ermöglicht die Konfiguration der Grafik und Steuerung sowie die Auswahl<br />
<strong>eine</strong>r anderen Höhenkarte.<br />
Das Erstellen <strong>eine</strong>s solchen Dialoges g<strong>est</strong>altet sich recht einfach: Die Steuerelemente wie z.B.<br />
Buttons oder Kontrollkästchen lassen sich im Resourceneditor von Visual C++ auf einfachste<br />
Weise platzieren und editieren. Mit dem MFC-Klassenassistenten lassen sich den<br />
Steuerelementen Objekte zuweisen – jedes Steuerelement hat <strong>eine</strong> eigene Klasse. So lassen<br />
sich beim Start des Programms die gewählten Einstellungen abfragen und auswählen.<br />
Beispielsweise gibt es die für Kontrollkästchen die Klasse CButton, <strong>mit</strong> deren Methode<br />
GetCheck() sich der Status als bool-Variable abfragen lässt, also angewählt oder nicht<br />
angewählt.<br />
Als Optionen stehen zur Verfügung:<br />
1. Grafikoptionen:<br />
- Es lässt sich die Auflösung wählen. Zur Auswahl stehen, 640*480, 800*600 und<br />
1024*768<br />
- Das Programm kann im Vollbild- oder im Fenstermodus g<strong>est</strong>artet werden.<br />
- 60 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
- Antialiasing (Kantenglättung) kann aktiviert werden, sofern die Grafikkarte dies<br />
beherrscht.<br />
2. Steuerungsoptionen:<br />
Die Steuerung per Tastatur und per Fernbedienung lassen sich einzeln ein- oder<br />
ausschalten.<br />
3. Laden <strong>eine</strong>r Heightmap:<br />
Es lässt sich statt der beigefügten Höhenkarte <strong>eine</strong> andere laden. Diese muss im 24bit-<br />
BMP-Format vorliegen.<br />
8.2 Laden der <strong>Landschaft</strong><br />
Die <strong>Landschaft</strong> liegt in <strong>eine</strong>m Graustufenbild im bmp-Format vor. Höhenwerte werden <strong>durch</strong><br />
unterschiedliche Graustufen repräsentiert. Der Einfachheit halber haben wir uns für <strong>eine</strong><br />
24bit-Bitmap entschieden. Dies erspart uns das Laden <strong>eine</strong>r Farbpalette (wie es bei <strong>eine</strong>r 8bit-<br />
Bitmap der Fall wäre) und ermöglicht uns, die Höhe <strong>eine</strong>s Punktes der <strong>Landschaft</strong> direkt aus<br />
den RGB-Werten zu lesen. Da bei jedem Grauton Rot-, Grün- und Blau-Anteil gleich sind,<br />
verschwendet die Datei natürlich Platz. Jedoch benutzen wir für unser Projekt ohnehin nur<br />
<strong>eine</strong> 257*257 Bitmap, was den benötigten F<strong>est</strong>plattenplatz vernachlässigbar macht.<br />
Das Laden der Bitmap g<strong>est</strong>altet sich relativ simpel. Das bmp-Format hat <strong>eine</strong>n 54 Byte<br />
großen Header. Dessen Daten sind für unsere Zwecke unwichtig, lediglich die Höhe und<br />
Breite der Bitmap werden gelesen, sodass wir ohne weiteres auch Bitmaps anderer Größe<br />
verwenden könnten (wichtig ist nur, dass die Größe 2 n + 1 ist).<br />
Danach folgen in der Datei nur die RGB-Werte, jeweils 1 Byte für Rot-, Grün- und Blau-<br />
Komponente. Zu beachten ist nur, dass nach jeder Zeile der Bitmap so viele Bytes gelesen<br />
werden, dass die Gesamtanzahl der gelesenen Farbbytes <strong>durch</strong> 4 teilbar ist.<br />
Der Code, der die Bitmap li<strong>est</strong>, befindet sich in der Datei Landscape.cpp in der Funktion<br />
LoadHeightFile.<br />
- 61 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.3 Erzeugung der <strong>3D</strong>-Daten<br />
Zunächst muss natürlich ein Vertexbuffer erzeugt werden. Dies geschieht <strong>mit</strong> <strong>eine</strong>m simplen<br />
Aufruf der Methode CreateVertexBuffer der Klasse MTRenderContext. Die Anzahl der<br />
Vertizes ergibt sich aus dem Produkt von Höhe und Breite der <strong>Landschaft</strong>.<br />
Positionsdaten:<br />
Auch das Erzeugen der Positionsdaten g<strong>est</strong>altet sich einfach. Die x- und die z-Position des<br />
Vertex ergeben sich von selbst, da wir ein quadratisches Feld von Höhenwerten haben. Die x-<br />
und z-Position sind also nur die Indizes in dieses Feld. Die y-Position ist der Wert an der<br />
entsprechenden Stelle im Feld.<br />
Normalen:<br />
Die Vertexnormalen werden berechnet, indem die Normalen der anliegenden Polygone<br />
ge<strong>mit</strong>telt werden.<br />
Farbwerte:<br />
Die Farbwerte ergeben sich aus der Höhe der <strong>Landschaft</strong>. Es gibt 4 Höhenbereiche: Zu unterst<br />
Sand, dann Gras, Fels und Schnee. Jeder dieser Bereiche fängt auf <strong>eine</strong>r b<strong>est</strong>immten Höhe an<br />
und hat <strong>eine</strong> Farbe zugeordnet. Farbwerte auf dazwischenliegenden Höhen werden linear aus<br />
den beiden angrenzenden Bereichen interpoliert.<br />
Texturkoordinaten:<br />
Auch die Texturkoordinaten sind höhenabhängig. Jeder der Farbbereiche hat auch <strong>eine</strong> Textur<br />
zugeordnet. Diese Texturen sind sogenannte Detailtexturen; sie enthalten Graustufen, die <strong>mit</strong><br />
den Vertexfarben gemischt werden, sodass diese unterschiedliche Tönungen erhalten und<br />
da<strong>mit</strong> detaillierter wirken. Alle 4 „logischen“ Texturen sind in <strong>eine</strong>r „physikalischen“<br />
enthalten. Welche logische Textur benutzt wird, hängt also nur von den Koordinaten ab.<br />
Es werden zwei Texturen verwendet und gemischt, da<strong>mit</strong> die harten Übergänge zwischen den<br />
Höhenstufen verschwinden.<br />
Den Code zur Erzeugung der Daten finden Sie in der Funktion CreateChunks in der Datei<br />
Landscape.cpp.<br />
- 62 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.4 Der Himmel<br />
Der Himmel wird <strong>durch</strong> <strong>eine</strong>n Würfel um den Betrachter herum repräsentiert. Dies mag nicht<br />
besonders realistisch klingen, doch <strong>eine</strong> gute Textur erweckt <strong>mit</strong> dieser Methode <strong>durch</strong>aus<br />
<strong>eine</strong>n glaubwürdigen Eindruck, und dieses Verfahren wird auch in vielen Computerspielen<br />
eingesetzt. Ein solcher Himmel nennt sich Skybox.<br />
Die Berechnung des Himmels ist sehr einfach. Es werden sechs Seiten aus jeweils vier<br />
Vertizes erzeugt. Jede Seite b<strong>est</strong>eht demnach aus zwei Polygonen und bekommt <strong>eine</strong> Textur<br />
zugewiesen. Die Texturen fügen sich nahtlos aneinander, sodass die geometrische Form des<br />
Würfels nicht zu erkennen ist.<br />
Wichtig ist, dass die Box sich <strong>mit</strong> dem Betrachter <strong>mit</strong>bewegt, da<strong>mit</strong> der Himmel immer gleich<br />
weit entfernt aussieht und so<strong>mit</strong> <strong>eine</strong>n glaubwürdigen Eindruck <strong>eine</strong>s Horizonts erweckt.<br />
Den Code finden Sie in den Dateien Skybox.h und Skybox.cpp.<br />
8.5 Der Demomodus<br />
Neben der Möglichkeit, <strong>durch</strong> die <strong>Landschaft</strong> zu laufen und zu fliegen, gibt es auch <strong>eine</strong>n<br />
Demomodus. In diesem Modus bewegt sich der Betrachter auf <strong>eine</strong>r zufallsberechneten<br />
Flugbahn. Dies funktioniert so, dass <strong>eine</strong> Anzahl von Wegpunkten berechnet wird, die dann<br />
als Splinekurve 1 interpoliert werden, sodass der Betrachter sich auf runden Bahnen <strong>durch</strong> die<br />
Welt bewegt. Die Berechnung der Wegpunkte erfolgt so, dass der Betrachter nicht <strong>mit</strong> der<br />
<strong>Landschaft</strong> kollidiert und diese auch nicht verlässt. Ausserdem ist <strong>eine</strong> maximale Flughöhe<br />
vorgegeben. Im Demomodus hat der Betrachter k<strong>eine</strong> weiteren Möglichkeiten zur Steuerung.<br />
Er kann nur den Modus verlassen oder das Programm beenden.<br />
1 Es werden sogenannte Her<strong>mit</strong>e Splines verwendet. Diese lassen sich ganz einfach aus der Differentialrechnung<br />
herleiten. Mehr dazu unter [8]<br />
- 63 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.6 Die Kontrollen<br />
Das Programm lässt sich per Tastatur, Fernbedienung oder beidem steuern. Hier die<br />
Tastenbelegungen:<br />
8.6.1 Fernbedienung:<br />
Beenden des Programms<br />
E/A<br />
Store<br />
vorwärts bewegen<br />
1 2 3<br />
4 5 6<br />
7 8 9<br />
nach oben drehen<br />
nach unten drehen<br />
nach rechts drehen<br />
rückwärts bewegen<br />
nach links drehen<br />
Demo-Rundflug<br />
starten/beenden<br />
Index Timer Radio<br />
Mute TV/Sat A/B<br />
laufen/fliegen<br />
umschalten<br />
Audio<br />
Video<br />
- 64 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.6.2 Tastatur:<br />
W<br />
S<br />
D<br />
F<br />
↑<br />
↓<br />
←<br />
→<br />
Vorwärts bewegen<br />
Rückwärts bewegen<br />
Demo-Rundflug starten / beenden<br />
Laufen/fliegen umschalten<br />
Nach oben drehen<br />
Nach unten drehen<br />
Nach links drehen<br />
Nach rechts drehen<br />
8.7 Das Hauptprogramm<br />
Das Hauptprogramm wird aufgerufen sobald im Optionsmenü der OK-Button betätigt wird.<br />
Es findet in der OnOk()-Methode des Optionsdialoges statt, wird also beim Klick automatisch<br />
aufgerufen.<br />
Da wir nichts davon halten, jede Programmzeile in ein Struktogramm zu übertragen, haben<br />
wir das Struktogramm stark vereinfacht, sodass die Vorgehensweise, aber nicht un<strong>mit</strong>telbar<br />
der Code, ersichtlich wird. Danach folgt der ausführlich dokumentierte Code, wobei wir von<br />
Visual C++ automatisch generierten Code ausgelassen haben, da dieser unwesentlich ist.<br />
- 65 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.7.1 Das Struktogramm<br />
Hauptprogramm<br />
Fensterklasse registrieren und Fenster erzeugen<br />
Render-DLL laden<br />
Renderer registrieren und aktivieren<br />
RenderContext erzeugen<br />
<strong>Landschaft</strong> erstellen und dem RenderContext hinzufügen<br />
Himmel erstellen und dem RenderContext hinzufügen<br />
Kamera aktivieren und Position setzen<br />
V24 initialisieren<br />
Endlosschleife<br />
Windows-Nachricht empfangen<br />
J<br />
Schleife verlassen<br />
J<br />
V24 Bytes lesen<br />
für alle Bytes, die empfangen wurden<br />
2 oder 6<br />
vorwärts<br />
bewegen<br />
J<br />
Demo fortsetzen<br />
J<br />
Kollision verhindern<br />
Grafik rendern<br />
4 oder 8<br />
rückwärts<br />
bewegen<br />
1 oder<br />
5<br />
links<br />
drehen<br />
3 oder<br />
7<br />
rechts<br />
drehen<br />
9<br />
Quit-Message<br />
absetzen<br />
Nachricht = Quit?<br />
Nachricht an die Fensterprozedur weiterleiten<br />
18<br />
nach<br />
oben<br />
gucken<br />
Demomodus?<br />
Flug-/Demomodus?<br />
20<br />
nach<br />
unten<br />
gucken<br />
Position auf Bodenhöhe setzen<br />
Fernbedienungssteuerung aktiviert?<br />
25<br />
Demomodus<br />
starten/beenden<br />
aktuelles Byte<br />
26<br />
Umschalten<br />
Laufen/Fliegen<br />
N<br />
N<br />
N<br />
N<br />
- 66 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
8.7.2 Der Code<br />
// T<strong>est</strong>appDlg.cpp : Implementierungsdatei<br />
//<br />
#define RPC_NO_WINDOWS_H<br />
#define WIN32_LEAN_AND_MEAN<br />
#include "stdafx.h"<br />
#include "T<strong>est</strong>app.h"<br />
#include "T<strong>est</strong>appDlg.h"<br />
#include <br />
#include "Her<strong>mit</strong>eSpline.h"<br />
#ifdef _DEBUG<br />
#define new DEBUG_NEW<br />
#undef THIS_FILE<br />
static char THIS_FILE[] = __FILE__;<br />
#endif<br />
// Startposition des Betrachters (der Kamera)<br />
#define STARTX 128.0f<br />
#define STARTZ 128.0f<br />
// die Distanz, die der Betrachter <strong>mit</strong> <strong>eine</strong>m Tastendruck zurücklegt<br />
#define MOVEMENTSPEED (1.0f)<br />
// der Winkel, um den sich der Betrachter <strong>mit</strong> <strong>eine</strong>m Tastendruck dreht<br />
// (Bogenmaß)<br />
#define TURNINGSPEED (0.1f)<br />
// Höhe der Kamera über dem Boden<br />
#define CAMHEIGHT 7.0f<br />
// Nummer der Wegpunkte im Demo(Flyby)-Modus<br />
#define NUMWAYPOINTS 1000<br />
// maximale Flughöhe für den Demomodus<br />
#define MAXFLIGHTHEIGHT 120.0f<br />
// Geschwindigkeit im Demomodus<br />
#define FLYBYSPEED 0.15f<br />
// ungefähre Distanz zwischen 2 Wegpunkten<br />
#define WAYPOINTDISTANCE 50.0f<br />
// Multiplikator für die Splinenormalen<br />
#define NORMALMULTIPLIER 100.0f<br />
// Exponent für die Zufallswerte, die zur Winkelberechnung im Demomodus<br />
// berechnet werden<br />
#define ANGLEEXPONENT 3.0f<br />
// maximaler Höhenunterschied zwischen zwei Wegpunkten<br />
#define WAYPOINTHEIGHT (WAYPOINTDISTANCE / 1.7f)<br />
/* diese Distanz wird in der Kollisionsabfrage addiert um sie sicherer zu<br />
machen (weil die Collision()-Funktion nur auf Kollisionen auf der Geraden<br />
zwischen den Wegpunkten, nicht auf dem Spline prüft*/<br />
#define SAFETYHEIGHT 10.0f<br />
MTCamera<br />
g_camera("cam"); // die Betrachterkamera<br />
MTLandscape * g_land; // Pointer auf die <strong>Landschaft</strong><br />
bool g_flying; // Fliegen wir?<br />
bool g_flyby; // sind wir im Flyby-Demomodus?<br />
// unsere Wegpunkte für den Demomodus<br />
MTVector<strong>3D</strong> g_waypoints[NUMWAYPOINTS];<br />
// Index des aktuellen Wegpunkts<br />
int<br />
g_currentwaypoint;<br />
- 67 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
// Pointer auf die aktuelle Splinekurve<br />
Her<strong>mit</strong>eSpline * g_currentspline;<br />
// Interpolationswert für den Spline<br />
float g_flybyinterpolator = 0.0f;<br />
// Tastatursteuerung an?<br />
bool g_keyboard;<br />
///////////////////////////////////////////////////////////////////////////<br />
Hier folgt von Visual C++ generierter Code, den wir ausgelassen haben, da<br />
er nichts zur Sache tut.<br />
///////////////////////////////////////////////////////////////////////////<br />
BOOL CT<strong>est</strong>appDlg::OnInitDialog()<br />
{<br />
CDialog::OnInitDialog();<br />
// Symbol für dieses Dialogfeld f<strong>est</strong>legen. Wird automatisch erledigt<br />
// wenn das Hauptfenster der Anwendung kein Dialogfeld ist<br />
SetIcon(m_hIcon, TRUE);<br />
// Großes Symbol verwenden<br />
SetIcon(m_hIcon, FALSE);<br />
// Kl<strong>eine</strong>s Symbol verwenden<br />
// ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen<br />
// Standard-Datei setzen<br />
m_heightfileedit.SetSel(0, m_heightfileedit.GetLi<strong>mit</strong>Text());<br />
m_heightfileedit.ReplaceSel("height.bmp");<br />
m_resolution.SetCurSel(0);<br />
m_keyboard.SetCheck(true);<br />
m_remote.SetCheck(true);<br />
GetCurrentDirectory(1023, m_directory);<br />
}<br />
return TRUE;<br />
///////////////////////////////////////////////////////////////////////////<br />
Hier folgt von Visual C++ generierter Code, den wir ausgelassen haben, da<br />
er nichts zur Sache tut.<br />
///////////////////////////////////////////////////////////////////////////<br />
/**************************************************************************<br />
** Diese Funktion bewegt die Betrachterkamera um MOVEMENTSPEED Einheiten.**<br />
** Im Flugmodus einfach in Richtung des Betrachtungsvektors, im Laufmodus**<br />
** an der <strong>Landschaft</strong> entlang. Das Flag forward gibt an, ob die <strong>Bewegung</strong> **<br />
** vorwärts oder rückwärts erfolgen soll. **<br />
**************************************************************************/<br />
void Move(bool forward)<br />
{<br />
if(g_flying)<br />
{<br />
/* im Flugmodus bewegen wir uns einfach in Richtung des<br />
Betrachtunsvektors der Kamera (bzw entgegengesetzt, je nach<br />
forward-Flag)*/<br />
float direction = 1.0f;<br />
if(!forward) direction = -1.0f;<br />
g_camera.SetPosition(g_camera.GetPosition() +<br />
- 68 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
}<br />
else<br />
{<br />
MTVector<strong>3D</strong> vec;<br />
float dist;<br />
float dx, dy, dz;<br />
direction * MOVEMENTSPEED *<br />
g_camera.GetLookAtVector());<br />
}<br />
}<br />
// Positionsunterschied für <strong>Bewegung</strong> nach vorne berechnen<br />
dx = g_camera.GetLookAtVector().x;<br />
dz = g_camera.GetLookAtVector().z;<br />
dy = g_land->GetAltitude(g_camera.GetPosition().x+dx,<br />
g_camera.GetPosition().z+dz) –<br />
g_land->GetAltitude(g_camera.GetPosition().x,<br />
g_camera.GetPosition().z);<br />
// Richtung umkehren, wenn rückwärts<br />
if(!forward)<br />
{<br />
dx *= -1;<br />
dy *= -1;<br />
dz *= -1;<br />
}<br />
// Positionsunterschied angleichen (auf die Länge von<br />
// MOVEMENTSPEED)<br />
dist = sqrt(dx*dx + dy*dy + dz*dz);<br />
dist /= MOVEMENTSPEED;<br />
dx /= dist; dy /= dist; dz /= dist;<br />
// Position berechnen und setzen<br />
float xpos = g_camera.GetPosition().x;<br />
float zpos = g_camera.GetPosition().z;<br />
xpos += dx;<br />
zpos += dz;<br />
g_camera.SetPosition(MTVector<strong>3D</strong>(xpos, g_land->GetAltitude(xpos,<br />
zpos) + CAMHEIGHT, zpos));<br />
///////////////////////////////////////////////////////////////////////////<br />
void RotateUp()<br />
{<br />
// Um den Rechtsvektor (die lokale x-Achse der Kamera) rotieren<br />
g_camera.Rotate(g_camera.GetRightVector(), TURNINGSPEED);<br />
}<br />
///////////////////////////////////////////////////////////////////////////<br />
void RotateDown()<br />
{<br />
// Um den Rechtsvektor (die lokale x-Achse der Kamera) rotieren<br />
g_camera.Rotate(g_camera.GetRightVector(), -TURNINGSPEED);<br />
}<br />
///////////////////////////////////////////////////////////////////////////<br />
void RotateLeft()<br />
- 69 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
{<br />
}<br />
// um die y-Achse rotieren<br />
g_camera.Rotate(MTVector<strong>3D</strong>(0,1,0), TURNINGSPEED);<br />
///////////////////////////////////////////////////////////////////////////<br />
void RotateRight()<br />
{<br />
// um die y-Achse rotieren<br />
g_camera.Rotate(MTVector<strong>3D</strong>(0,1,0), -TURNINGSPEED);<br />
}<br />
///////////////////////////////////////////////////////////////////////////<br />
/**************************************************************************<br />
** Diese Funktion prüft ob die Gerade von pos1 nach pos2 die <strong>Landschaft</strong> **<br />
** (ungefähr) schneidet. **<br />
**************************************************************************/<br />
bool Collision(MTVector<strong>3D</strong> pos1, MTVector<strong>3D</strong> pos2)<br />
{<br />
// Entfernungsunterschied zwischen den beiden Positionen berechnen<br />
float dist = MTVec3Length(pos2-pos1);<br />
// Schrittweite berechnen (sollte ca 1 sein)<br />
MTVector<strong>3D</strong> step = (pos2-pos1) / dist;<br />
// die gesamte Distanz <strong>durch</strong>laufen...<br />
for(int i = 0; i < (int)dist; i++)<br />
{<br />
// ... und prüfen ob die Gerade unterhalb der <strong>Landschaft</strong> liegt.<br />
// Wenn ja, Kollision<br />
pos1 += step;<br />
if(pos1.y < g_land->GetAltitude(pos1.x, pos1.z) +<br />
CAMHEIGHT + SAFETYHEIGHT)<br />
return true;<br />
}<br />
}<br />
// k<strong>eine</strong> Kollision<br />
return false;<br />
///////////////////////////////////////////////////////////////////////////<br />
/**************************************************************************<br />
** Diese Funktion startet den Demomodus. Dazu berechnet sie alle **<br />
** Wegpunkte und berechnet das erste Spline. **<br />
**************************************************************************/<br />
void StartFlyBy()<br />
{<br />
float height;<br />
float angle;<br />
// Winkel auf Zufallswert setzen<br />
float oldangle = ((float)rand() / (float)RAND_MAX) * 2 * MT_PI;<br />
float dangle;<br />
// Startposition zur Berechnung ist die Kameraposition<br />
float oldx = g_camera.GetPosition().x;<br />
float oldz = g_camera.GetPosition().z;<br />
float oldy = g_camera.GetPosition().y + SAFETYHEIGHT;<br />
float x, z;<br />
- 70 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
float turndirection;<br />
// Zufallswerte initialisieren<br />
srand(time(NULL));<br />
// Flag setzen, da<strong>mit</strong> der Flug später fortgesetzt wird.<br />
g_flyby = true;<br />
// alle Wegpunkte...<br />
for(int i = 0; i < NUMWAYPOINTS; i++)<br />
{<br />
do<br />
{<br />
do<br />
{<br />
// Winkelunterschied im Bereich von 0 bis 180°.<br />
// Zufallswert wird potenziert,<br />
// um öfter kl<strong>eine</strong>re Winkel zu erzeugen.<br />
dangle = pow(((float)rand() / (float)RAND_MAX),<br />
ANGLEEXPONENT) * MT_PI;<br />
// Drehrichtung b<strong>est</strong>immen<br />
turndirection = floor(((float)rand() /<br />
(float)RAND_MAX) + 0.5f) * 2.0f -1.0f;<br />
dangle *= turndirection;<br />
angle = oldangle + dangle;<br />
// neue x/y-Position = alte Position +<br />
// WAYPOINTDISTANCE Einheiten in Winkelrichtung<br />
g_waypoints[i].x = oldx + sin(angle) *<br />
WAYPOINTDISTANCE;<br />
g_waypoints[i].z = oldz + cos(angle) *<br />
WAYPOINTDISTANCE;<br />
x = g_waypoints[i].x;<br />
z = g_waypoints[i].z;<br />
// so lange bis die Position nicht ausserhalb der Karte<br />
// liegt<br />
} while(x > (float)(g_land->GetWidth()) || x < 0<br />
|| z > (float)(g_land->GetHeight()) || z < 0);<br />
// Höhe im Bereich zwischen der Höhe der <strong>Landschaft</strong> und<br />
// der maximalen Flughöhe<br />
height = g_land->GetAltitude(x, z) + CAMHEIGHT;<br />
g_waypoints[i].y = ((float)rand() / (float)RAND_MAX) *<br />
(MAXFLIGHTHEIGHT - height) + height;<br />
// solange bis k<strong>eine</strong> Kollision <strong>mit</strong> der <strong>Landschaft</strong> entsteht und<br />
// der Höhenunterschied zwischen der alten und der neuen<br />
// Position nicht größer als WAYPOINTHEIGHT ist<br />
} while(Collision(MTVector<strong>3D</strong>(oldx, oldy, oldz),<br />
MTVector<strong>3D</strong>(x, g_waypoints[i].y, z)) ||<br />
fabs(g_waypoints[i].y - oldy)>WAYPOINTHEIGHT);<br />
}<br />
// alte Werte für den nächsten Durchlauf setzen<br />
oldx = x;<br />
oldy = g_waypoints[i].y;<br />
oldz = z;<br />
oldangle = angle;<br />
// Wegpunkt initialisieren<br />
- 71 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
g_currentwaypoint = 0;<br />
}<br />
// ersten Spline berechnen, die Normale (Lookat) in <strong>eine</strong>m Punkt<br />
// ergibt sich aus dem normalisierten Vektor zwischen dem Wegpunkt<br />
// davor und dem danach, multipliziert <strong>mit</strong> NORMALMULTIPLIER<br />
MTVector<strong>3D</strong> lookat = g_waypoints[1] - g_waypoints[NUMWAYPOINTS-1];<br />
MTVec3Normalize(lookat);<br />
lookat *= NORMALMULTIPLIER;<br />
MTVector<strong>3D</strong> lookat2 = g_waypoints[2] - g_waypoints[0];<br />
MTVec3Normalize(lookat2);<br />
lookat2 *= NORMALMULTIPLIER;<br />
g_currentspline = new Her<strong>mit</strong>eSpline(g_waypoints[0],<br />
lookat, g_waypoints[1], lookat2);<br />
///////////////////////////////////////////////////////////////////////////<br />
/**************************************************************************<br />
** Diese Funktion setzt den Demomodus fort. Hierzu wird die nächste **<br />
** Position auf dem aktuellen Spline berechnet. Wenn das Ende des **<br />
** aktuellen Splines ereicht ist, wird der nächste Spline berechnet. **<br />
**************************************************************************/<br />
void ContinueFlyBy()<br />
{<br />
// Interpolator basierend auf vergangener Zeit und<br />
// Fluggeschwindigkeit erhöhen<br />
g_flybyinterpolator += FLYBYSPEED * g_rendermanager->GetFrameTime();<br />
// ein Spline liegt immer zwischen 0 und 1, also bei 1 nächster<br />
// Spline<br />
while(g_flybyinterpolator > 1.0f)<br />
{<br />
g_flybyinterpolator -= 1.0f;<br />
g_currentwaypoint++;<br />
// wenn wir den letzten Wegpunkt erreicht haben, landen wir<br />
// wieder beim ersten<br />
g_currentwaypoint %= NUMWAYPOINTS;<br />
// alten Spline löschen<br />
delete g_currentspline;<br />
}<br />
// neuen Spline wie oben berechnen<br />
MTVector<strong>3D</strong> lookat, lookat2;<br />
lookat = g_waypoints[(g_currentwaypoint+1)%NUMWAYPOINTS] –<br />
g_waypoints[(g_currentwaypoint-1)%NUMWAYPOINTS];<br />
MTVec3Normalize(lookat);<br />
lookat *= NORMALMULTIPLIER;<br />
lookat2 = g_waypoints[(g_currentwaypoint+2)%NUMWAYPOINTS] –<br />
g_waypoints[(g_currentwaypoint)%NUMWAYPOINTS];<br />
MTVec3Normalize(lookat2);<br />
lookat2 *= NORMALMULTIPLIER;<br />
g_currentspline = new<br />
Her<strong>mit</strong>eSpline(g_waypoints[g_currentwaypoint],<br />
lookat, g_waypoints[(g_currentwaypoint+1)%NUMWAYPOINTS],<br />
lookat2);<br />
// Kameraposition basierend auf Interpolationswert des Splines setzen<br />
g_camera.SetPosition(g_currentspline->GetValue(g_flybyinterpolator));<br />
- 72 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
// Lookat-Punkt (Ortsvektor) für die Kamera berechnen<br />
// = aktuelle Position + kl<strong>eine</strong>s Stück weiter auf dem Spline<br />
MTVector<strong>3D</strong> look;<br />
look = g_currentspline->GetValue(g_flybyinterpolator) –<br />
g_currentspline->GetValue(g_flybyinterpolator-0.001f);<br />
MTVec3Normalize(look);<br />
look += g_currentspline->GetValue(g_flybyinterpolator);<br />
// leicht nach unten gucken<br />
look.y -= 0.45f;<br />
}<br />
// Lookat für die Kamera setzen.<br />
g_camera.SetLookAt(look, MTVector<strong>3D</strong>(0,1,0));<br />
///////////////////////////////////////////////////////////////////////////<br />
LRESULT CALLBACK MTWindowProc(HWND hwnd,<br />
UINT msg,<br />
WPARAM wparam,<br />
LPARAM lparam)<br />
{<br />
PAINTSTRUCT ps;<br />
HDC<br />
hdc;<br />
switch(msg)<br />
{<br />
case WM_CREATE:<br />
{<br />
return 0;<br />
} break;<br />
case WM_PAINT:<br />
{<br />
hdc = BeginPaint(hwnd,&ps);<br />
EndPaint(hwnd,&ps);<br />
return 0;<br />
} break;<br />
case WM_DESTROY:<br />
{<br />
PostQuitMessage(0);<br />
return 0;<br />
} break;<br />
case WM_KEYDOWN:<br />
{<br />
if(g_keyboard)<br />
{<br />
if(g_flyby)<br />
{<br />
// im Flyby-Demomodus nur auf Escape und D<br />
// (Demomodus beenden) reagieren<br />
if(wparam == 'D')<br />
{<br />
g_flyby = false;<br />
delete g_currentspline;<br />
}<br />
- 73 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
}<br />
} break;<br />
}<br />
else<br />
{<br />
}<br />
else if(wparam == VK_ESCAPE)<br />
PostQuitMessage(0);<br />
switch(wparam)<br />
{<br />
// vorwärts bewegen<br />
case 'W':<br />
Move(true); break;<br />
// rückwärts bewegen<br />
case 'S':<br />
Move(false); break;<br />
// Flugmodus verlassen<br />
case 'F':<br />
g_flying = !g_flying; break;<br />
// Demomodus starten<br />
case 'D':<br />
StartFlyBy(); break;<br />
// nach oben gucken<br />
case VK_UP: RotateUp(); break;<br />
// nach unten gucken<br />
case VK_DOWN: RotateDown(); break;<br />
// nach links drehen<br />
case VK_LEFT: RotateLeft(); break;<br />
// nach rechts drehen<br />
case VK_RIGHT: RotateRight(); break;<br />
// Programm verlassen<br />
case VK_ESCAPE: PostQuitMessage(0); break;<br />
}<br />
}<br />
default:break;<br />
}<br />
return (DefWindowProc(hwnd, msg, wparam, lparam));<br />
///////////////////////////////////////////////////////////////////////////<br />
////////<br />
/**************************************************************************<br />
** In dieser Methode findet das Hauptprogramm statt. Es startet beim **<br />
** Druck auf OK. Es erstellt ein neues Fenster, startet die <strong>3D</strong>-Engine, **<br />
** initialisiert V24 und <strong>Landschaft</strong> und läuft dann in <strong>eine</strong>r **<br />
** while-Schleife, wo Userinputs abgearbeitet werden und gerendert wird. **<br />
**************************************************************************/<br />
void CT<strong>est</strong>appDlg::OnOK()<br />
{<br />
WNDCLASS winclass;<br />
MSG<br />
msg;<br />
HINSTANCE hinstance = GetModuleHandle(NULL);<br />
HWND hwnd;<br />
// Fensterklassen-Struktur füllen<br />
winclass.style = CS_DBLCLKS | CS_OWNDC |<br />
CS_HREDRAW | CS_VREDRAW;<br />
winclass.lpfnWndProc = MTWindowProc;<br />
winclass.cbClsExtra = 0;<br />
winclass.cbWndExtra = 0;<br />
winclass.hInstance = hinstance;<br />
winclass.hIcon<br />
= LoadIcon(NULL, IDI_APPLICATION);<br />
- 74 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);<br />
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);<br />
winclass.lpszMenuName = NULL;<br />
winclass.lpszClassName = "ManiaTech Window";<br />
// Fensterklasse registrieren<br />
if (!RegisterClass(&winclass))<br />
{<br />
exit(1);<br />
}<br />
// Tastatursteuerung?<br />
g_keyboard = m_keyboard.GetCheck();<br />
// gewählte Auflösung der ComboBox auswerten<br />
switch(m_resolution.GetCurSel())<br />
{<br />
case 0: m_width = 640; m_height = 480; break;<br />
case 1: m_width = 800; m_height = 600; break;<br />
case 2: m_width = 1024; m_height = 768; break;<br />
}<br />
// Fenster erzeugen<br />
if (!(hwnd = CreateWindow("ManiaTech Window",<br />
"ManiaTech Landscape Demo",<br />
WS_OVERLAPPEDWINDOW | WS_VISIBLE,<br />
0,0,<br />
m_width,<br />
m_height,<br />
NULL,<br />
NULL,<br />
hinstance,<br />
NULL)))<br />
{<br />
}<br />
// DLLManager-Klasse. Lädt und verwaltet DLLs.<br />
MTDLLManager<br />
dllman;<br />
// Der Rendermanager ist das Rendering-Subsystem der Engine<br />
MTRenderManager<br />
renderman;<br />
// das Interface zum Rendern von Grafik<br />
MTRenderContext *<br />
context;<br />
// Pointer auf die Create-Funktion der DLL<br />
MTRenderManager::CreateRenderContextPtr<br />
createcontextptr;<br />
// Pointer auf die Render-DLL<br />
MTDLL *<br />
dx8dll;<br />
// Schleifenzähler<br />
int i;<br />
// aktuelles Verzeichnis setzen. (wird bei Initialisierung gesichert,<br />
// da der Windows-Öffnen-Dialog das Verzeichnis ändert)<br />
SetCurrentDirectory(m_directory);<br />
// Laden der Render-DLL<br />
dx8dll = dllman.LoadDLL("RendererDirect<strong>3D</strong>8.dll");<br />
assert(dx8dll);<br />
// Create-Pointer aus der DLL-Laden. Die Funktion, auf die der<br />
// Pointer zeigt, ist für die Erstellung <strong>eine</strong>s RenderContexts<br />
- 75 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
// zuständig.<br />
createcontextptr = (MTRenderManager::CreateRenderContextPtr)<br />
dx8dll->GetFunctionAddress("CreateContext");<br />
assert(createcontextptr);<br />
// Renderer registrieren und auswählen<br />
renderman.SetRenderer(renderman.RegisterRenderer("DirectX8",<br />
createcontextptr));<br />
// RenderContext erzeugen<br />
context = renderman.CreateRenderContext(&hwnd,<br />
m_fullscreenbutton.GetCheck(), m_width,<br />
m_height, 1, MTFMT_A8R8G8B8, MTFMT_D24X8,<br />
((int)m_aliasbutton.GetCheck())*2, 75,<br />
false, false);<br />
assert(context);<br />
// Dateinamen für die Heightmap aus dem Editfeld holen<br />
char heightfile[1024];<br />
m_heightfileedit.GetLine(0, heightfile, 255);<br />
// <strong>Landschaft</strong> erzeugen ...<br />
MTLandscape landscape(context, string(heightfile), 7);<br />
// ... und dem RenderContext hinzufügen<br />
context->AddRenderable(&landscape);<br />
// globalen Pointer auf die <strong>Landschaft</strong> setzen<br />
g_land = &landscape;<br />
// Himmel erzeugen ...<br />
MTSkyBox skybox(context, 500, "camTOP.jpg", "camWE.jpg", "camNO.jpg",<br />
"camEA.jpg", "camSO.jpg", "camBOT.jpg");<br />
// ... und dem RenderContext hinzufügen<br />
context->AddRenderable(&skybox);<br />
// Kamera setzen<br />
context->SetCamera(&g_camera);<br />
// Startposition des Betrachters setzen<br />
g_camera.SetPosition(MTVector<strong>3D</strong>(STARTX, landscape.GetAltitude(STARTX,<br />
STARTZ)+CAMHEIGHT, STARTZ));<br />
// V24 initialisieren<br />
MTV24Windows v24;<br />
Byte bytes[256]; // Puffer für die Bytes von der V24<br />
DWord numbytes;<br />
if(m_remote.GetCheck())<br />
{<br />
v24.Init(1, 9600, 8, V24STOP_ONE, V24PARITY_NONE);<br />
}<br />
while(1)<br />
{<br />
// Nachrichten abarbeiten<br />
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))<br />
{<br />
// Programm beenden, wenn wir <strong>eine</strong> WM_QUIT Message<br />
// bekommen<br />
if (msg.message == WM_QUIT)<br />
{<br />
break;<br />
- 76 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
}<br />
TranslateMessage(&msg);<br />
}<br />
// Nachrichten an die Windowprozedur senden<br />
// (MTWindowProc)<br />
DispatchMessage(&msg);<br />
if(m_remote.GetCheck())<br />
{<br />
// Bytes von der V24 lesen...<br />
numbytes = v24.Read(bytes, 256);<br />
for(i = 0; i < numbytes; i++)<br />
{<br />
// ... und interpretieren<br />
if(g_flyby)<br />
{<br />
// im Flyby-Demomodus nur auf Quit und<br />
// Demomodus-Taste<br />
// (Demo beenden) reagieren<br />
if(bytes[i] == 25)<br />
{<br />
g_flyby = false;<br />
delete g_currentspline;<br />
}<br />
else if(bytes[i] == 9)<br />
PostQuitMessage(0);<br />
}<br />
else<br />
{<br />
switch(bytes[i])<br />
{<br />
// vorwärts bewegen<br />
case 1: case 5: Move(true); break;<br />
// rückwärts bewegen<br />
case 3: case 7: Move(false); break;<br />
// Flugmodus verlassen<br />
case 26:<br />
g_flying = !g_flying; break;<br />
// Demomodus starten<br />
case 25:<br />
StartFlyBy(); break;<br />
// nach oben gucken<br />
case 18:<br />
RotateUp(); break;<br />
// nach unten gucken<br />
case 20:<br />
RotateDown(); break;<br />
// nach links drehen<br />
case 2: case 6: RotateLeft(); break;<br />
// nach rechts drehen<br />
case 4: case 8: RotateRight(); break;<br />
// Programm verlassen<br />
case 9: PostQuitMessage(0); break;<br />
}<br />
}<br />
}<br />
}<br />
// Im Demomodus Flug fortsetzen<br />
if(g_flyby)<br />
ContinueFlyBy();<br />
- 77 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
// Kollisionsverhinderung (Betrachter bleibt CAMHEIGHT<br />
// Einheiten über dem Boden)<br />
if(g_flying || g_flyby)<br />
{<br />
float xpos = g_camera.GetPosition().x;<br />
float zpos = g_camera.GetPosition().z;<br />
}<br />
else<br />
{<br />
}<br />
if(g_camera.GetPosition().y <<br />
landscape.GetAltitude(xpos, zpos) + CAMHEIGHT)<br />
g_camera.SetPosition(g_camera.GetPosition() +<br />
MTVector<strong>3D</strong>(0, landscape.GetAltitude(xpos, zpos) +<br />
CAMHEIGHT- g_camera.GetPosition().y, 0));<br />
// im Laufmodus auf Bodenhöhe bleiben (ist normalerweise<br />
// schon der Fall,<br />
// aber nicht beim Wechsel von Flug- zu Laufmodus)<br />
float x = g_camera.GetPosition().x, z =<br />
g_camera.GetPosition().z;<br />
g_camera.SetPosition(MTVector<strong>3D</strong>(x,<br />
g_land->GetAltitude(x,z) + CAMHEIGHT,z));<br />
}<br />
// Rendern (Zeichnen der Grafik)<br />
if(g_rendermanager->Render())<br />
MessageBox("rendering error", "error", 0);<br />
}<br />
CDialog::OnOK();<br />
///////////////////////////////////////////////////////////////////////////<br />
////////<br />
// Diese Methode wird bei Drücken des "Durchsuchen"-Buttons aufgerufen.<br />
// Sie zeigt <strong>eine</strong>n Datei öffnen Dialog an, in dem man die Heightmap wählen<br />
kann.<br />
void CT<strong>est</strong>appDlg::OnButton1()<br />
{<br />
OPENFILENAME openfile;<br />
char filename[1024] = "";<br />
}<br />
memset(&openfile, 0, sizeof(OPENFILENAME));<br />
openfile.lStructSize = sizeof(OPENFILENAME);<br />
openfile.lpstrFilter = "24-Bit Bitmap (*.BMP)\0*.BMP\0";<br />
openfile.nFilterIndex = 1;<br />
openfile.lpstrFile = filename;<br />
openfile.nMaxFile = 1023;<br />
openfile.lpstrTitle = "Heightmap laden";<br />
if(GetOpenFileName(&openfile))<br />
{ m_heightfileedit.SetSel(0, m_heightfileedit.GetLi<strong>mit</strong>Text());<br />
m_heightfileedit.ReplaceSel(openfile.lpstrFile);<br />
}<br />
- 78 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
9. Schlussbemerkung<br />
Ja, hier ist es nun, unser fertig dokumentiertes Projekt. Es kostete Geld, viel Zeit und Mühe<br />
und nicht zuletzt auch noch Nerven (diese Dokumentation wurde 2 Stunden vor dem<br />
Abgabetermin gedruckt und gebunden).<br />
Auf dem langen und beschwerlichen Weg von <strong>eine</strong>r Projektidee bis zur Verwirklichung<br />
vergingen viele, viele Stunden vor Kopfschmerzen bereitendem Quellcode und vor den nicht<br />
immer korrekt konfigurierten Rechnern der EST, die es uns des Öfteren nicht gerade leicht<br />
machten.<br />
Doch zuletzt bleibt nur zu sagen, dass wir beide selbst erstaunt waren, wie viele Probleme wir<br />
im Stande waren, selbst anzugehen und zu lösen. Wir hatten definitiv viel Spaß und Freude an<br />
der Entwicklung dieses Vorhabens und wir sind uns sicher, dass dies auch nicht unser letztes<br />
Projekt in dieser Fachrichtung sein wird.<br />
Ganz besonderer Dank geht an dieser Stelle an Herrn Hambsch und Herrn Czok, unsere<br />
beiden Betreuer, welche uns bei Problemen wie „Darf man in <strong>eine</strong>m Vorwort emotional<br />
werden?“ tatkräftig zur Seite standen; und natürlich auch bei echten Problemen.<br />
Kontakt <strong>mit</strong> den Erschaffern dieses Projektes erhalten Sie unter:<br />
Alexander Fuchs<br />
grunzgrunz@web.de<br />
<strong>David</strong> <strong>Zaadstra</strong><br />
mythomania@gmx.de<br />
- 79 -
Projektarbeit 2002, BKIK 2/1<br />
<strong>David</strong> <strong>Zaadstra</strong>, Alexander Fuchs<br />
<strong>Bewegung</strong> <strong>durch</strong> <strong>eine</strong> <strong>3D</strong>-<strong>Landschaft</strong> <strong>mit</strong> Fernbedienung<br />
Elektronikschule Tettnang<br />
10. Anhang<br />
10.1 Literaturverweise<br />
[1] Additional Class Notes on Triangle Rasterization, 2000<br />
http://cs-people.bu.edu/jisidoro/cs480/additional/trirast5.doc<br />
[2] Tomas Möller und Eric Haines, Realtime Rendering, S. 23 ff, 1999,<br />
ISBN: 1-56881-101-2<br />
[3] Oliver Vornberger und Olaf Müller, <strong>3D</strong>-Transformationen, Juli 2000<br />
h t t p : / / w w w -l e h r e . i n f o r m a t i k . u n i -<br />
o s n a b r u e c k . d e / ~ c g / 2 0 0 0 / s k r i p t / 1 3 _ 3 D _ T r a n s f o r m a t i o n e n . h t m l<br />
[4] Tomas Möller und Eric Haines, Realtime Rendering, ISBN: 1-56881-101-2<br />
[5] Mark Segal und Kurt Akeley, The OpenGL Graphics System: A Specification<br />
(Version 1.3), 2001<br />
h t t p : / / w w w . o p e n g l . o r g / d e v e l o p e r s / d o c u m e n t a t i o n / v e r s i o n 1 _ 3 / g l s p e c 1 3 . p d f<br />
[6] Appeal, Outcast<br />
h t t p : / / w w w . o u t c a s t -g a m e . c o m / g e r m a n / h t m l / i n d e x . h t m<br />
[7] The good old days – Comanche<br />
h t t p : / / w w w . g o o d d a y s . o r g / c o m a n c h e s c r e e n s 3 . s h t m l<br />
[8] Pat Hanrahan und G. Scott Owen, Her<strong>mit</strong>e Splines, 1998-99<br />
h t t p : / / w w w . s i g g r a p h . o r g / e d u c a t i o n / m a t e r i a l s / H y p e r G r a p h / m o d e l i n g / s p l i n e s / h<br />
e r m i t e . h t m<br />
- 80 -
IR-Empfänger für Fernbedienungen<br />
IR-Receiver for Remote Control Systems<br />
SFH 5110<br />
SFH 5111<br />
Beschreibung<br />
SFH 5110 und SFH 5111 sind Infrarot-Empfänger<br />
für die Erkennung von Signalen aus Infrarot-Fernbedienungssystemen<br />
und b<strong>est</strong>ehen aus Fotodiode,<br />
Vorverstärker, automatischer Verstärkungsregelung,<br />
Bandpaß-Filter und Demodulator. Das<br />
schwarz eingefärbte Gehäuse dient zur Unterdrückung<br />
des Tageslichteinflusses.<br />
Wesentliche Merkmale<br />
• IC <strong>mit</strong> monolithisch integrierter Fotodiode<br />
(Ein-Chip Lösung)<br />
• Speziell geeignet für Anwendungen von<br />
940 nm<br />
• Hohe Empfindlichkeit<br />
• Variable Bandpaß-Filterfrequenz<br />
• TTL und CMOS kompatibel<br />
• Ausgang: aktiv „Low“<br />
• K<strong>eine</strong> externe Beschaltung nötig<br />
Anwendungen<br />
• Empfänger in Fernbedienungen für TV,<br />
Videorekorder, HiFi, Satellitenempfänger und<br />
CD-Spieler<br />
• Optischer Schalter<br />
Description<br />
SFH 5110 and SFH 5111 are IR receivers to detect<br />
light from infrared remote control systems.<br />
The IC includes photodiode, preamplifier, automatic<br />
gain control, bandpass and demodulator.<br />
The black-colored package is designed as daylight-cutoff<br />
filter.<br />
Features<br />
• IC with monolithic integrated photodiode (single<br />
chip solution)<br />
• Especially suitable for applications of 940 nm<br />
• High sensitivity<br />
• Various bandpass filter frequency<br />
• TTL and CMOS compatibility<br />
• Output: active Low<br />
• No external components necessary<br />
Applications<br />
• Remote control module for TV sets, VCRs, hi-fi<br />
audio receivers, SAT receivers and compact<br />
disk players<br />
• Optical Switch<br />
2001-02-22 1
SFH 5110, SFH 5111<br />
Typ<br />
Trägerfrequ.<br />
B<strong>est</strong>ellnr.<br />
Typ<br />
Trägerfrequ.<br />
B<strong>est</strong>ellnr.<br />
Type<br />
Carrier<br />
Frequency<br />
kHz<br />
Ordering Code<br />
Type<br />
Carrier<br />
Frequency<br />
kHz<br />
Ordering Code<br />
SFH 5110-30 30 Q62702-P5088 SFH 5111-30 30 Q62702-P5257<br />
SFH 5110-33 33 Q62702-P5089 SFH 5111-33 33 Q62702-P5258<br />
SFH 5110-36 36 Q62702-P5090 SFH 5111-36 36 Q62702-P5259<br />
SFH 5110-38 38 Q62702-P5091 SFH 5111-38 38 Q62702-P5260<br />
SFH 5110-40 40 Q62702-P5092 SFH 5111-40 40 Q62702-P5261<br />
Grenzwerte (T A = 25 °C)<br />
Maximum Ratings<br />
Bezeichnung<br />
Parameter<br />
Betriebs- und Lagertemperatur<br />
Operation and storage temperature range<br />
Betriebsspannung<br />
Supply voltage<br />
Betriebsstrom<br />
Supply current<br />
Ausgangsspannung<br />
Output voltage<br />
Ausgangsstrom<br />
Output current<br />
Verlustleistung<br />
Total power dissipation, T A ≤ 85 °C<br />
Symbol<br />
Symbol<br />
Wert<br />
Value<br />
T op<br />
T stg<br />
– 10 … + 75<br />
– 30 … + 100<br />
V CC 6.3 V<br />
Einheit<br />
Unit<br />
°C<br />
I CC 5 mA<br />
V OUT<br />
6.3 V<br />
I OUT 3 mA<br />
P tot 50 mW<br />
Empfohlener Arbeitsbereich<br />
Recommended Operating Conditions<br />
Bezeichnung<br />
Parameter<br />
Betriebstemperatur<br />
Operating temperature<br />
Betriebsspannung<br />
Supply Voltage<br />
Symbol<br />
Symbol<br />
Wert<br />
Value<br />
min. typ. max.<br />
T op – 10 – 75 °C<br />
V cc 4.5 5.0 5.5 V<br />
Einheit<br />
Unit<br />
2001-02-22 2
SFH 5110, SFH 5111<br />
Kennwerte (T A = 25 °C)<br />
Characteristics<br />
Bezeichnung<br />
Parameter<br />
Stromaufnahme, V CC = 5 V, E = 0<br />
Current consumption<br />
Wellenlänge der max. Fotoempfindlichkeit<br />
Wavelength of max. sensitivity<br />
Spektraler Bereich der Fotoempfindlichkeit<br />
Spectral range of sensitivity<br />
Ausgangsspannung<br />
Output voltage<br />
Output “High” - (I q = 10 µA)<br />
Output “Low” - (I q = 500 µA)<br />
Trägerfrequenz<br />
Carrier frequency<br />
Min. B<strong>est</strong>rahlungsstärke (T<strong>est</strong>signal, s. Fig. 3)<br />
Min. Threshold irradiance (t<strong>est</strong> signal, see Fig. 3)<br />
f = f 0 , t p,I = 600 µs<br />
Min. Eingangspulsbreite „ON“<br />
(T<strong>est</strong>signal, s. Fig. 3) 1)<br />
Min. Input pulse width “ON”<br />
(t<strong>est</strong> signal, see Fig. 3) 1)<br />
Ausgangspulsbreite „ON“<br />
(T<strong>est</strong>signal, s. Fig. 3)<br />
Output pulse width “ON” (t<strong>est</strong> signal, see Fig. 3,<br />
E e = 1 mW/m 2 )<br />
50%-Filterbandbreite, f = f O , E V = 0, V CC = 5 V<br />
50%-Filter bandwidth<br />
Symbol<br />
Symbol<br />
Wert<br />
Value<br />
min. typ. max.<br />
I CC – 1.3 – mA<br />
λ s max – 940 – nm<br />
λ 830 – 1100 nm<br />
V OUT high V S – 0.5<br />
V OUT low –<br />
–<br />
–<br />
f 0 – 30<br />
33<br />
36<br />
38<br />
40<br />
–<br />
0.5<br />
Einheit<br />
Unit<br />
V<br />
– kHz<br />
E e min – 0.35 0.5 mW/m 2<br />
t p,I 6/f O – – µs<br />
t p,O<br />
t p,I – t p,I µs<br />
– 6/f O + 6/f O<br />
∆f 50% 3 – 6 kHz<br />
1)<br />
1)<br />
Die volle Empfindlichkeit wird bei <strong>eine</strong>r Burstlänge von mind<strong>est</strong>ens 6 Pulsen erreicht. Die Reichweite bei<br />
Verwendung <strong>eine</strong>s typischen Senders (SFH 4510/SFH 4515, I F = 500 mA) beträgt etwa 30 m.<br />
A minimum burst length of 6 pulses is necessary for full sensitivity. The transmission distance with a typical<br />
trans<strong>mit</strong>ter (SFH 4510/SFH 4515, I F = 500 mA) is about 30 m.<br />
2001-02-22 3
_<<br />
SFH 5110, SFH 5111<br />
Input<br />
Control<br />
Circuit<br />
23 kΩ<br />
V CC<br />
PIN<br />
AGC<br />
Bandpass<br />
Demodulator<br />
OUT<br />
GND<br />
OHF00404<br />
Figure 1<br />
Blockschaltbild<br />
Block Diagram<br />
SFH 5110-xx<br />
SFH 5111-xx<br />
3<br />
*)<br />
4.7 µF<br />
*)<br />
10 k Ω<br />
optional<br />
+5 V<br />
1<br />
µC<br />
2<br />
*) only necessary to suppress power supply disturbances<br />
GND<br />
OHF00430<br />
Figure 2<br />
Externe Beschaltung<br />
External Circuit<br />
2001-02-22 4
SFH 5110, SFH 5111<br />
t p, I t off, I t p, I<br />
Trans<strong>mit</strong>ter<br />
t<br />
= t p, o<br />
t<br />
± 6 / f o<br />
p, I<br />
Detector<br />
(Output Signal)<br />
t<br />
Burst wave: carrier frequency f o , Duty cycle = 0.5<br />
OHF00399<br />
Figure 3<br />
Optisches T<strong>est</strong>signal<br />
Optical T<strong>est</strong> Signal<br />
2001-02-22 5
SFH 5110, SFH 5111<br />
Relative Luminous Sensitivity<br />
S rel = f (λ)<br />
100<br />
%<br />
90<br />
OHF00400<br />
Vertical Directivity ϕ y<br />
1<br />
0.9<br />
OHF00401<br />
Horizontal Directivity ϕ x<br />
OHF00402<br />
1<br />
0.9<br />
80<br />
0.8<br />
0.8<br />
70<br />
/E e<br />
0.7<br />
/E e<br />
0.7<br />
S rel<br />
10<br />
60<br />
E e, min<br />
0.6<br />
E e, min<br />
0.6<br />
50<br />
0.5<br />
0.5<br />
40<br />
0.4<br />
0.4<br />
30<br />
0.3<br />
0.3<br />
20<br />
0.2<br />
0.2<br />
0.1<br />
0.1<br />
0<br />
700 800 900 1000 nm 1100<br />
λ<br />
Relative Sensitivity E e /E e, min = f (f 0 )<br />
OHF00403<br />
5<br />
0<br />
0<br />
30 60 [ ] 90<br />
ϕ<br />
0<br />
0<br />
30 60 [ ] 90<br />
ϕ<br />
4.5<br />
4<br />
E e /E e, min<br />
3.5<br />
3<br />
2.5<br />
2<br />
1.5<br />
1<br />
f o -4 fo-2<br />
f o fo+2<br />
kHz f o +4<br />
Center Frequency<br />
2001-02-22 6
SFH 5110, SFH 5111<br />
Maßzeichnung<br />
Package Outlines<br />
2.54 (0.100)<br />
0.6 (0.024)<br />
0.4 (0.016)<br />
2.54 (0.100)<br />
1<br />
2<br />
3<br />
24.4 (0.961)<br />
23.4 (0.921)<br />
32.0 (1.260)<br />
6.1 (0.240)<br />
5.9 (0.232)<br />
1.3 (0.051)<br />
1.1 (0.043)<br />
3.0 (0.118)<br />
2.8 (0.110)<br />
6.1 (0.240)<br />
5.9 (0.232)<br />
0.5 (0.020)<br />
0.3 (0.012)<br />
3.5 (0.138)<br />
3.3 (0.130)<br />
30.0 (1.181)<br />
5.1 (0.201)<br />
4.9 (0.193)<br />
Pinning SFH 5110<br />
1 OUT<br />
2 GND<br />
3<br />
V CC<br />
Pinning SFH 5111<br />
1 OUT<br />
2 V CC<br />
3 GND<br />
GEOY6985<br />
Maße werden wie folgt angegeben: mm (inch) / Dimensions are specified as follows: mm (inch).<br />
2001-02-22 7
SFH 5110, SFH 5111<br />
Published by OSRAM Opto Semiconductors GmbH & Co. OHG<br />
Wernerwerkstrasse 2, D-93049 Regensburg<br />
© All Rights Reserved.<br />
Attention please!<br />
The information describes the type of component and shall not be considered as assured characteristics.<br />
Terms of delivery and rights to change design reserved. Due to technical requirements components may contain<br />
dangerous substances. For information on the types in qu<strong>est</strong>ion please contact our Sales Organization.<br />
Packing<br />
Please use the recycling operators known to you. We can also help you – get in touch with your near<strong>est</strong> sales office.<br />
By agreement we will take packing material back, if it is sorted. You must bear the costs of transport. For packing<br />
material that is returned to us unsorted or which we are not obliged to accept, we shall have to invoice you for any costs<br />
incurred.<br />
Components used in life-support devices or systems must be expressly authorized for such purpose! Critical<br />
components 1 , may only be used in life-support devices or systems 2 with the express written approval of OSRAM OS.<br />
1 A critical component is a component usedin a life-support device or system whose failure can reasonably be expected<br />
to cause the failure of that life-support device or system, or to affect its safety or effectiveness of that device or system.<br />
2 Life support devices or systems are intended (a) to be implanted in the human body, or (b) to support and/or maintain<br />
and sustain human life. If they fail, it is reasonable to assume that the health of the user may be endangered.<br />
2001-02-22 8