05.11.2013 Aufrufe

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

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!