„Aufbau einer Funkübertragung per Mikrocontroller“
Transcrição
„Aufbau einer Funkübertragung per Mikrocontroller“
„Aufbau einer Funkübertragung per Mikrocontroller“ Ralf Töppner und Daniel Schüller Koblenz, den 25. April 2005 Inhaltsverzeichnis 1 Hintergrund und Ziel der Studienarbeit 3 1.1 Vorwort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Zielsetzung und Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2 Praktische Grundlagen 5 2.1 Mikrocontroller Atmel 2313 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2 Max RS232 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3 Versuchsboards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.3.1 Programmierstecker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.3.2 Das Atmel-Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Funkmodule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.4.1 Der Sender: EasyTX 434Mhz . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.4.2 Der Empfänger: EasyRX 434Mhz . . . . . . . . . . . . . . . . . . . . . . . 16 Die Software und deren Bezugsquellen . . . . . . . . . . . . . . . . . . . . . . . . 16 2.4 2.5 3 Theoretische Betrachtung 3.1 17 Datenübertragungs-Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.1 Signalcodierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.2 Paketaufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.1.3 Datendurchsatz der Verbindung . . . . . . . . . . . . . . . . . . . . . . . . 21 Modellierung der Zeit: Die Wait-Methode . . . . . . . . . . . . . . . . . . . . . . 23 3.2.1 Die Funktion im Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3 Implementierung der Sende-Routine . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.4 Implementierung der Empfangs-Routine . . . . . . . . . . . . . . . . . . . . . . . 30 3.5 Übertragungs-Protokoll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.2 1 INHALTSVERZEICHNIS 3.6 3.5.1 Der aktive Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.5.2 Der passive Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Grundgerüst des Programms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4 Test der Funkstrecke 62 5 Chronologie 66 5.1 Verlauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 5.2 Beurteilung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6 Anhang 72 6.1 Anhang A: Schaltplan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6.2 Anhang B: Literaturverzeichnis und CD-Rom Anlage . . . . . . . . . . . . . . . . 74 6.3 Anhang C: Erklärung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 2 Kapitel 1 Hintergrund und Ziel der Studienarbeit 1.1 Vorwort Das Stichwort “wireless“ ist nicht nur eines der Leitmotive der diesjährigen CeBIT 2005, sondern auch ein uns täglich umgebendes Gebiet der Informatik. An der Universität ist es ein WLan, welches uns das Surfen im Internet ermöglicht, zu Hause stehen ein Cordless-Desktop zur Verfügung und das Handy kann per Bluetooth Daten senden und empfangen. Doch nicht nur in diesen bereits technisch sehr versierten Bereichen spielen Funkstrecken eine Rolle, denn selbst das normale tragbare Telefon, die Funkfernbedienung für den Pkw als auch der Garagentoröffner basieren auf einer Datenübertragung per Funk. Dies waren nur einige wenige Beispiel, doch verdeutlichen diese, wie wichtig und vor allem komfortabel Steuerungen und Datenübertragung per Funk geworden sind. Nicht umsonst vereint der Begriff “wireless“ gleich eine ganze Reihe von Gebieten der Informatik unter sich und ein Ende der Entwicklung ist bei weitem nicht abzusehen. Der Ruf nach immer mehr Komfort und Unabhängigkeit wird niemals verstummen. Doch was genau steckt hinter dieser Technik und welche Gesichtspunkte der Informatik müssen dabei beachtet werden? Im kommenden Sommersemester 2005 wird im physikalischen Bereich der Informatik unserer Universität ein Praktikum stattfinden, welches ebenfalls auf eine Datenübertragung per Funk 3 1.2. ZIELSETZUNG UND AUFGABENSTELLUNG zurückgreifen könnte. Einem auf einer Bahn fahrenden Modellauto sollen Steueranweisungen übertragen werden und auch das Auto selbst soll Daten an einen Rechner senden können. Natürlich riecht dies förmlich nach einer Funkstrecke, doch ist es überhaupt möglich, eine solche Verbindung mit elementaren Grundlagen der Informatik aufzubauen? Die oben genannten Fragestellungen liegen unserer Arbeit zu Grunde und stellen die Basis für die Studienarbeit. 1.2 Zielsetzung und Aufgabenstellung Ziel unserer Studienarbeit ist es eine Funkstrecke mit elementaren Mitteln der Informatik aufzubauen und Daten zu übertragen, ohne dabei auf bereits realisierte Mittel zurückzugreifen. Diese Datenübertragung soll asynchron realisiert werden. Die zur Verfügung stehende Hardware sind zwei Mikrocontroller und Funkmodule. Die dazu notwendigen Schaltungen sind aufzubauen sowie eine selbständige Einarbeitung in die Programmierung des Mikrocontrollers notwendig. Abschließend soll die praktische Relevanz der aufgebauten Strecke sowie deren Einsetzbarkeit im oben genannten Praktikum beurteilt werden. 4 Kapitel 2 Praktische Grundlagen 2.1 Mikrocontroller Atmel 2313 Zur Umsetzung unserer Studienarbeit entscheiden wir uns für die Verwendung des Mikrocontrollers AT90S2313 der Firma Atmel. Dieser Mikrocontroller aus der AVR-Familie ist ein in CMOS gefertigter 8-bit Controller und bietet uns alle notwendigen Funktionen zum Aufbau der Funkstrecke. Natürlich hätten wir auch einen ATmega Controller aus der nächst größeren AVR-Serie verwenden können, doch schien uns der AT90S2313 die sinnvollste und kostengünstigste Wahl zu sein. Ebenso ist dieser für den Einstieg in die Welt der Mikrocontroller sehr gut geeignet und erleichterte uns die Einarbeitungsphase. Die Grundarchitektur aller AVR-Micro-Controller ist nicht wesentlich verschieden und die Unterschiede sind lediglich in der Ausstattung zu finden. Der Atmel AT90S2313 ist ein RISC-Controller und somit auf die wesentlichen Befehle im Vergleich zur CISC-Architektur reduziert. Die Tatsache, das die fehlenden komplexeren Befehle durch geschickte Programmierung erzeugt werden müssen, wird oftmals als Nachteil der RISCArchitektur gesehen, doch fiel dies im Verlaufe unserer Arbeit nicht weiter ins Gewicht. Die Architektur des Atmel AT90S2313 wird in Abb. 2.2 dargestellt. Auf die genaue Funktionsweise und den Aufbau des Mikrocontrollers werden wir an dieser Stelle nicht eingehen und verweisen auf die Beschreibung und das Datenblatt der Firma Atmel1 . Dennoch wollen wir die einzelnen Pins und die von uns verwendete Belegung kurz erklären. 1 siehe unter www.atmel.com 5 2.1. MIKROCONTROLLER ATMEL 2313 Bei der Pinbelegung des Mikrocontroller müssen wir unterscheiden zwischen den vom Hersteller fest vorgegebenen und den von uns frei wählbaren Pins. Diese Belegung wird im folgenden kurz erklärt. vorgegebene Pins: VCC (5V Spannungsversorgung) GND (Masse) SCK,MISO,MOSI,RESET (Programmierpins) XTAL 1 + 2 (Quarz) PD0, PD1 (TXD u. RXD der Uart) PD2, PD3 (Int-Pins) (alle Pins von D und B können trotzdem als I/O Pins verwendet werden) frei wählbare Pins: PortB: 0 bis 7 (auch Programmierpins) Abbildung 2.1: Atmel-Schaltbild PortD: 0 bis 6 Die Belegung der Ports sei jedem Programmierer freigestellt. Wir implementierten folgende Variante: Pin D6 Daten-Empfangspin Pin B0 Enable-Pin des Senders Pin B1 Daten-Sendepin Pin D2 Interrupt-Pin zum Verbindungsaufbau Pin D3,D4,D5 Kontroll-LEDs 6 2.1. MIKROCONTROLLER ATMEL 2313 Zahlen und Fakten zum AT90S2313: 2k Bytes programmierbarer Flash Speicher 128 Bytes EEPROM 128 Bytes SRAM 15 I/O Ports 32 Register 2 flexible Timer: 8 und 16 Bit Interner und externer Interrupt Voll-Duplex UART Watchdog-Timer Serielles SPI-Interface 2 Power-Save-Modi 118 Assembler-Befehle Abbildung 2.2: ATA90S2313 Architektur 7 2.1. MIKROCONTROLLER ATMEL 2313 Wir entschieden uns dafür den Atmel AT90S2313 im Assemblercode zu programmieren und griffen dazu auf das „Instruction Set“ 2 von Atmel zurück. Um die Lesbarkeit und das Verständnis der später folgenden Quelltexte zu erhöhen erklären wir nun kurz die wesentlichen von uns verwendeten Befehle: allg. Befehle Beschreibung LDI Rg, Wert Wert wird ins Rg geladen IN Rg, IO-Rg legt den Wert des IO-Rg in Rg ab OUT IO-Rg, Rg gibt das Rg an ein IO-Register aus MOV Rg2, Rg1 kopiert den Wert von Rg1 nach Rg2 ADD Rg2, Rg1 addiert auf Rg2 den Wert von Rg1 Vergleiche: CPI Rg1, Wert vergleicht das Register mit angegebenem Wert anschliessend muss ein Sprungbefehl folgen CP Rg1, Rg2 wie CPI, nur werden zwei Rg verglichen Sprünge: BREQ Label nach CPI (CP) bei Gleichheit nach ’Label’ springen BRLO Label nach CPI (CP) Sprung nach ’Label’ wenn Rg1 ≤ Rg2 ist BRNE Label nach CPI (CP) Sprung nach ’Label’ wenn Rg1 6= Rg2 ist RJMP Label unbedingter Sprung nach ’Label’ Pins/Bits setzen/löschen in einem Rg: CBI IO-Rg, Pin setzt den Pin im IO-Rg auf Null SBI IO-RG, Pin setzt den Pin im IO-RG auf Eins CBR Rg, Rg wie CBI in Bezug auf ein nicht IO-Rg SBR Rg, Rg wie SBI in Bezug auf ein nicht IO-Rg Unterprogrammaufruf: RCALL Label ruft ein Unterprogramm beginnend ab Label auf RET verläßt ein aufgerufenes Unterprogramm 2 siehe unter www.atmel.com 8 2.1. MIKROCONTROLLER ATMEL 2313 Überspringen des nächsten Befehls: SBRC Rg, Pin überspringt den nächsten Befehl wenn Pin im Rg gleich Null ist SBRS Rg, Pin wie SBRC wenn Pin im Rg gleich Eins SBIS IO-Rg, Pin wie SBRS in Bezug auf ein IO-Rg SBIC IO-RG, Pin wie SBRC in Bezug auf ein IO-Rg weitere Befehle: INC Rg erhöht das Rg um den Wert 1 DEC Rg zieht dem Rg den Wert 1 ab CLR Rg setzt das gesamte Rg auf Null LSL Rg shiftet das Rg links, das höchste Bit wird im Carry übernommen LSR Rg shiftet das Rg rechts, das niedrigste Bit wird im Carry übernommen ROR Rg shiftet Rg rechts und übernimmt den Carry als höchstes Bit das rausgeshiftete niedrigste Bit wird in den Carry übernommen SEC setz das Carry-Flag CLC löscht das Carry-Flag Dies sind die wesentlichen Befehle zur Programmierung des Atmels, auf alle weiteren Anmerkungen zum Assemblercode werden wir direkt am Quelltext eingehen. 9 2.2. MAX RS232 2.2 Max RS232 Um Daten vom Mikrocontroller über die serielle Schnittstelle(RS232) an den PC senden zu können benötigen wir einen Pegelkonverter, welcher die auf Seiten des Atmels vorliegenden TTLPegel (0 bis 5V) auf RS232-Pegel (-12V bis +12V) umsetzt. Diese Funktion übernimmt der MAX-Baustein, denn ihm genügt eine 5V Versorgungsspannung um die angesprochene PegelWandlung vorzunehmen. Die benötigten +12V und -12V werden von dem IC selbst erzeugt. Als externe Beschaltung genügen 4 Elkos mit 4,7 µF. Die beiden im MAX-Baustein integrierten DC-DC-Wandler dienen zum einen zur Spannungsverdopplung(+10V) und zum anderen der Spannungsinvertierung(-10V). Um eine korrekte Wandlung eines TTL Signals auf ein RS232 Signal zu ermöglichen muss eine Pegelumsetzung auf die höheren Pegel erfolgen und zusätzlich das Signal invertiert werden. Eine logische 1 entspricht bei TTL 5V und bei RS232 -12V, eine logisch 0 wird anlog von TTL 0V auf RS232 +12V umgesetzt. Bei der Pegelumsetzung setzt der MAX alle ihm übergebenen Bits in den jeweils notwendigen Pegel um, d.h. auch eventuell vorhandene Start-, Stop- oder ParityBits des UART Protokolls werden an die RS232 bzw. an den Mikrocontroller weitergereicht. Diese nehmen die Weiterverarbeitung selbst vor. Abbildung 2.3: Pinbelegung des Max-Baustein 10 2.3. VERSUCHSBOARDS 2.3 Versuchsboards Das Grundlayout der Platinen orientiert sich am Schaltplan von Walter Rowalt3 und wurde unseren Anforderungen entsprechend angepasst. Dabei reduzierten wir die Bauteile auf das notwendige Minimum und integrierten Steckplätze für die Funkmodule sowie Bauteile um deren Spannungsversorgung zu gewährleisten. Der Programmierstecker wurde ebenfalls auf die wesentlichen Elemente vereinfacht (siehe Abb. 2.4). 2.3.1 Programmierstecker Die Bauteile des Programmiersteckers beschränken sich auf ein Stück Lochrasterplatine, einen 25-poligen D-Sub Stecker(90 Grad abgewinkelt) und drei 220 Ohm Widerstände. Um nun die Verbindung zum Board herzustellen verwendeten wir eine einfache Steckerleiste, deren Gegenstück auf die Boards gelötet wurde. Im Vergleich zum Programmierstecker von Walter Rowalt konnten wir auf die Anschlüsse der Pins 4 bis 8 verzichten, da sie in unserer Arbeit keine Funktionalität zugewiesen bekamen. Ein altes Parallel-Port Kabel aus einem PC, an welchem man die aufgeführten Widerstände einlötet, würde ebenso Abbildung 2.4: Programmierstecker 3 siehe seinen (preiswerten) Dienst verrichten. auch www.rowalt.de 11 2.3. VERSUCHSBOARDS 2.3.2 Das Atmel-Board Unsere modifizierte Version des Atmel-Boards wird in Abb. 2.5 dargestellt. Eine größere und übersichtlichere Version befindet sind im Anhang A in Kapitel 6.1 auf Seite 73. Eine genaue Liste der Bauteile befindet sich gleich im Anschluss an diesen Abschnitt. Abbildung 2.5: Schaltplan Für die Studienarbeit erstellten wir zwei Boards nach obigem Schaltplan und versorgten sie mit einer externen Spannung von +5V. Da eine Funkverbindung immer erst durch drücken des IntSW-Tasters aufgebaut werden sollte, entschieden wir uns dafür, diesen Taster nur auf einem der beiden Boards zu integrieren und von diesem Board aus die Funkverbindung zu initiieren. Der dort ebenfalls vorhandene Kondensator dient lediglich zur Spannungsglättung um die TasterPrellung auszugleichen, da sonst mit einem Druck gleich mehrfach der Atmel-Interrupt ausgelöst worden wäre. Die Anordnung der Bauteile auf den beiden Boards war ebenfalls leicht unterschiedlich, dennoch nicht wesentlich vom Schaltplan verschieden. Um ebenfalls von außen in der Lage zu sein einen Reset des Atmels durchzuführen, integrierten wir einen weiteren Taster, welcher beim Drücken den Reset-Pin des Atmels auf Masse zog und damit einen Reset auslöste. 12 2.3. VERSUCHSBOARDS Die aufgeführten LEDs dienten zur Überwachung der einzelnen Protokoll-Zustände4 , in welchen sich die Atmels beim Programmablauf befinden konnten. Ein weiteres Problem stellte sich beim Integrieren der Funkmodule auf die Boards bzgl. der Spannunsgversorgung. Während der Empfänger ebenfalls mit +5V arbeitete, lag der Spannungsbereich des Senders bei 2,2V bis maximal 4V, was also sichergestellt sein musste, um den Sender nicht zu beschädigen. Dies hätten wir durch einen Spannungsteiler oder eine bzw. mehrere Dioden erreichen können. Wir entschieden uns wegen der einfacheren praktischen Handhabung für zwei Dioden in Reihenschaltung und erhielten somit die notwendige Versorgungsspannung des Senders. Da wir jeweils einen Sender und einen Empfänger der gleichen Frequenz auf den Boards integrierten, mussten wir den inaktiven Sender auch hardwareseitig ausschalten, da sonst der Empfänger auf dem jeweils gleichen Board gestört worden wäre. Aus diesem Grund entschieden wir uns für die Verwendung eines Transistors, welcher je nach Bedarf den Enable-Eingang des Senders auf Masse zog. Diesen Sachverhalt verdeutlicht Abb. 2.6 nochmals. Nebenstehend sehen wir einen Teil des Schaltplanes vergrößert und um einige Angaben ergänzt. Die Versorgungsspannung für den Sender wird durch den Spannungsabfall an den beiden Dioden (je Diode etwa 0.7V) gewährleistet und der Sender damit versorgt. Da nun am Enable-Eingang des Funkmoduls die gleiche Spannung anliegen muss, wird diese über einen Transistor geschaltet. Liegt von Seiten des Atmel ein Low-Pegel an der Basis des Transistors an, so geht der Widerstand zw. Collector und Emitter gegen unendlich, der Transistor sperrt, der Strom kann nicht mehr über den Emitter Abbildung 2.6: Spannungsversorgung abfließen. Damit liegen die angesprochenen 3,6V zw. und Enable-Eingang Enable- und GND-Pin an. Im anderen Fall, ein HighPegel von Seiten des Atmels an der Basis, geht der Widerstand zw. Collector und Emitter gegen 0, der Strom fließt damit über den Emitter ab, zw. Enableund GND-Pin liegt damit keine Spannungsdifferenz mehr vor. 4 siehe auch Kapitel 3.5 auf Seite 42 13 2.3. VERSUCHSBOARDS Natürlich ist der von uns entwickelte Aufbau einer von vielen Möglichkeiten die Boards zu gestalten und es ist dem Entwickler freigestellt, welchen Weg er gerne gehen möchte. So könnte man den Anschluss der seriellen Schnittstelle z.B. auch über ein Com-Port-Kabel und eine Steckverbindung ermöglichen oder den Programmierstecker ebenfalls mit in den Schaltplan integrieren. Wir entschieden uns für die externe Variante, um das Layout so kompakt wie möglich zu gestalten. Die Bauteile für das Board: Lochrasterplatine Atmel AT90S2313 + Fassung MAX RS232 + Fassung Sub D Stecker 9 polig (90 Grad) Steckerleisten für Programmieradapter Klemmstecker für die Funkmodule 6 Kondensatoren 4,7 µF 2 Kondensatoren 33 pF 1 Quarz mit 9,216Mhz 3 Leds 2 Taster 2 Dioden 1 Transistor BC108A 2 Widerstände mit 1,8 und 3,8 kOhma a Erklärung siehe Abb 2.6 auf der vorherigen Seite Abb. 5.1 und Abb. 5.2 auf Seite 67 zeigen die fertigen Boards. 14 2.4. FUNKMODULE 2.4 Funkmodule Die Suche nach geeigneten Funkmodulen erwies sich wesentlich schwieriger als zunächst erwartet und schließlich entschieden wir uns für die Module der Serie “EasyTX/RX 434 FM“ der Firma “Ingenieurbetrieb Kunze Hochfrequenztechnik“. Diese Module stellen eine funktionale, aber nicht optimale Lösung dar, doch dazu später mehr. In den folgenden Beschreibungen werden wir nur auf die für unsere Studienarbeit relevanten Daten eingehen und verweisen für alle weiteren Informationen auf die Datenblätter des Herstellers5 . 2.4.1 Der Sender: EasyTX 434Mhz Die beiden Abbildungen 2.7 und 2.8 zeigen das Sende-Modul. Abbildung 2.7: Sender Abbildung 2.8: Sender-Schaltplan Über die Pins VCC und GND wurde die Versorgungsspannung von 3,6V angelegt, welches in Kapitel 2.3.2 auf Seite 12 bereits genauer erklärt worden ist. Das Modul arbeitet mit 434 Mhz und wird durch Anlegen der Versorgungsspannung am Enable-Pin in den Sende-Modus versetzt, anderenfalls wartet es im StandBy-Modus. Die zu übertragenden Daten werden am Data-Pin angelegt. Über die Pins LPD OUT und NC können weitere Informationen über z.B. die Stärke des Sendesignals abgelesen werden. Da diese Eigenschaften jedoch in unserer Arbeit nicht genutzt werden, verzichten wir an dieser Stelle genauer darauf einzugehen. Weitere Daten6 : Einschaltzeit: 0,8 bis 1 ms; Betriebsspannung: 2,2V bis 4V; empfohlene Baudrate: bis 40kbit/s; maximale Reichweite: 500m. 5 zu 6 in finden unter www.ihkf.de Relevanz für die Studienarbeit 15 2.5. DIE SOFTWARE UND DEREN BEZUGSQUELLEN 2.4.2 Der Empfänger: EasyRX 434Mhz Die beiden Abbildungen 2.9 und 2.10 zeigen das Empfangs-Modul. Abbildung 2.9: Empfänger Abbildung 2.10: Empfänger-Schaltplan Über die Pins VCC und GND wird die Versorgungsspannung von 5V angelegt. Alle weiteren Pin-Belegungen sind analog zu denen des Senders. Weitere Daten7 : Betriebsspanung: 4,5V bis 5,5V; empfohlene Baudrate: 0,5 bis 40kbit/s. 2.5 Die Software und deren Bezugsquellen Die Programme für den Mikrocontroller Atmel 2313 entwickelten wir auf einer MS Windows XP Umgebung im Assemblercode. Alle Bezugsquellen werden im Anschluss aufgelistet. Zur Entwicklung verwendeten wir das Atmel AVR Studio in der Version 4.10. Quelltexte wurde in *.asm Dateien gespeichert und die compilierten Hex-Dateien mit TwinAVR bzw. SP12 über den LPT Port auf den Atmel geschrieben. Jegliche Debug-Anweisungen nahmen wir über die Serielle Schnittstelle entgegen und ließen sie durch das Programm AVRTerm graphisch darstellen. Die Software bezogen wir über folgenden Quellen: AVR Studio 4.10 www.atmel.com SP12 und TwinAVR 1.1.0.1 (früher WinAVR) www.rowalt.de/mc/avr/progd.htm AVRTerm 1.2.0.4 www.rowalt.de/mc/avr/toolsd.htm Atmel AT90S2313 Datasheet www.atmel.com Atmel Instruction Set www.atmel.com 7 Einschaltzeit irrelevant, da nur Sender aktiviert bzw. deaktiviert wird 16 Kapitel 3 Theoretische Betrachtung 3.1 Datenübertragungs-Grundlagen In diesem Abschnitt wollen wir nun die theoretischen Grundlagen unserer Kommunikation betrachten. 3.1.1 Signalcodierung Eine unserer Vorgaben war es, die Datenübertragung asynchron zu realisieren. Dies bedeutet, dass die beiden kommunizierenden Knoten keinen gemeinsamen synchronen Takt aufweisen. Synchronität wird nur während der Übertragung eines Paketes hergestellt, was natürlich seine Vorund Nachteile hat. Ein Vorteil ist es, dass keine Funktionalität zur Verfügung gestellt werden muss, welche dafür Sorge trägt, dass die Knoten synchron laufen, d.h. es muss kein zusätzliches Taktsignal übertragen werden. Einen Nachteil handelt man sich mit der nun notwendigen Synchronisation ein, da einige Bits zusätzlich gesendet werden müssen, um diese möglich zu machen. Die Übertragungsrate ist also demnach bei einer asynchroner Übertragung niedriger als bei der synchronen Variante. Dies ist ein für unsere Arbeit sehr wichtiger Aspekt, da zum einen die Funkmodule keine dauerhaften Low- bzw. Highpegel aufrechterhalten und wir zum anderen somit die selbstsynchronisierende Eigenschaft des Manchester-Codes ausnutzen können. Sicherlich hätte man längere High- bzw. Low- Pegel durch Bitstuffing verhindern können, doch wäre dies eine we- 17 3.1. DATENÜBERTRAGUNGS-GRUNDLAGEN Abbildung 3.1: Manchester-Code sentlich komplexere Umsetzung gewesen, als die Verwendung des Manchester- Codes. Vor den Datenbytes wird nun zusätzlich ein Präambel vornweg geschaltet, an welcher sich der Empfänger synchronisieren und im Anschluss mit der Datenauswertung beginnen kann. Zur Übertragung unserer Daten entschieden wir uns daher für die Verwendung des ManchesterCodes. Hier entspricht ein Pegelwechsel innerhalb einer Bit-Dauer von Low nach High einer logischen Null, von High nach Low einer logischen Eins, dies zeigt Abb. 3.1. Damit haben wir in jedem Bit einen garantierten Pegelwechsel und verhindern somit lange High- oder Low-Pegel innerhalb der Paket-Übertragung. 3.1.2 Paketaufbau Ein gesamtes Datenpaket besteht generell aus 3 Byte, also 3 mal 8 Bit. Das erste Byte dient einzig und allein zur Synchronisation und ist ein Indikator für den Inhalt der beiden folgenden Bytes. Diese können zwei Arten von Daten enthalten: Protokolldaten oder die zu übertragenden Nutzdaten. Welches Datum nun vorliegt wird durch das letzte Bit des ersten Bytes, von uns Datentypbit genannt, indiziert. Das erste Byte enthält die Präambel und das angesprochene Datentypbit. Die Präambel besteht aus 7 Synchronisations-Bits, 6 mal eine logische Null gefolgt von einer logischen 1. Für diesen Paketaufbau entschieden wir uns aus mehreren Gründen. Die Länge der Präambel ergab sich aus praktischen Tests, da wir nicht garantieren konnten wie viele der ersten 7 Bits vollständig beim Empfänger ankommen. Dieser Wert war auf Grund der Funkmodule nie konstant. Das anschließende Datentypbit hilft bei der Auswertung der folgenden zwei Bytes. Enthält es eine 0, so sind die folgenden Daten Protokollanweisungen, enthält es eine 1, so sind die folgenden Daten reine Nutzdaten. Im Anschluss folgen nun genau 2 Byte aus zwei verschiedenen Gründen. Der erste Grund liegt darin, dass wenn wir nur ein Byte übertragen würden, unser Paket einen zu 18 3.1. DATENÜBERTRAGUNGS-GRUNDLAGEN Abbildung 3.2: Paketaufbau großen Overhead von insgesamt einem Byte hätte. Der zweite Grund war der uns im Hinterkopf verbliebene geplante Einsatz im anstehenden Projekt-Praktikum. Dort würde man zwei Bytes benötigen, um die Motorsteuerung sinnvoll zu realisieren. Abbildung 3.3: Paketaufbau auf dem Oszi. Die Synchronisation des Empfängers an der Präambel verhält sich wie folgt: Der Empfänger muss aus den ersten 6 logischen Nullen drei gleich lange Pegel der Form Low-High-Low erkennen. Danach wartet er auf einen genau doppelt so langen High-Pegel. Dieser Pegel wird mit dem letzten Wechsel von einer logischen Null auf eine logische Eins erzeugt, dies zeigt den nun folgenden Anfang der Nutzdaten an und verhindert, dass wir bei sehr früher Erkennung der ersten drei richtigen Pegel fälschlicherweise weitere Synchronisationspegel als Daten interpretieren könnten. 19 3.1. DATENÜBERTRAGUNGS-GRUNDLAGEN Erst wenn diese 4 Pegel fehlerfrei erkannt wurden beginnt der Empfänger mit dem Auslesen der folgenden Daten. Wurde dies nicht erreicht, meldet der Empfänger einen Timeout und versucht eine erneute Synchronisation am nächsten Paket. Gemessen wird je Bit genau zweimal, jeweils in der Mitte der beiden vorhandenen Pegel. Die Messzeitpunkte werden durch gezieltes Warten erreicht, da dem Empfänger die Pegelabstände bekannt sind. Um nun zu verhindern, dass sich der Empfänger in einem Fehlerfall an zufällig im Datenteil auftretenden logischen Nullen neu synchronisieren kann, entschieden wir uns dafür, die Pegelbreiten unterschiedlich lang zu gestalten. Die Pegel des ersten Bytes sind länger als die Pegel der beiden Datenbytes und somit ist es nicht möglich die passenden Synchronisationspegel fälschlicher Weise innerhalb eines Datenbytes zu messen, da deren Pegel die geforderte Länge unterschreiten. Damit ist sichergestellt, dass sich der Empfänger wirklich nur an der Präambel synchronisieren kann. Auf die genauen Pegelbreiten und den damit verbunden Kenngrößen wie der Datendurchsatz der Verbindung gehen wir in Kapitel 3.1.3 auf der nächsten Seite ein. Bei diesen Pegelbreiten ist natürlich auch zu beachten, dass die Pegel der Nutzdaten auf keinen Fall ganzzahlige Vielfache der Pegelbreiten des ersten Bytes einnehmen dürfen, denn ein solcher Pegel könnte als ein gültiger Wechsel von 0 nach 1 bzw. umgekehrt interpretiert werden. 20 3.1. DATENÜBERTRAGUNGS-GRUNDLAGEN 3.1.3 Datendurchsatz der Verbindung In diesem Abschnitt wollen wir kurz auf einige Kenngrößen und Leistungsmerkmale unserer FunkVerbindung eingehen. Wie bei allen Datenübertragungen ist die effektive Übertragungsrate eine aussagekräftige Größe, welche wir im folgenden nun berechnen wollen. Zunächst bestimmen wir die Dauer eines einzelnen Taktes und errechnen die Pegeldauer als auch die Gesamtpaketdauer daraus. Diese ist natürlich von den gewählten Pegelbreiten abhängig, deren Berechnung wir ebenfalls an dieser Stelle durchführen. Auf den Versuchsboards verwendeten wir einen Quarz mit 9,216 Mhz. Daraus ergibt sich die Dauer eines Taktes: T aktdauer = 1 = 109ns 9, 216M Hz (3.1) Auf Grund der verwendeten Manchester-Codierung besteht ein logischer Wert aus 2 Pegeln, jeweils einem Low-High- bzw. High-Low-Anteil. Die Dauer eines Pegels entspricht dem Zeitraum, in welchem der Datenpin des Mikrocontroller auf Low oder High gehalten wird. Dieser Zeitraum wird in Takten angegeben und entspricht somit der Wartetaktanzahl. Damit errechnet sich die Dauer für ein Bit wie fogt: Bitdauer = 2 ∗ W artetaktanzahl ∗ T aktdauer (3.2) Nun müssen wir unterscheiden zwischen den Pegelbreiten des ersten und denen der beiden folgenden Bytes. Diese Pegelbreiten werden durch die Methode Wait erzeugt, deren Funktionsweise im Kapitel 3.2 auf Seite 23 genauer erklärt wird. Die Methode Wait macht nichts anderes, als einen Pegel am Datenpin des Atmels zu erzeugen und diesen eine fest vorgegebene Anzahl an Takten aufrecht zu erhalten. Sie erwartet 2 Variablen: einen Multiplikator in Waitmul und eine Zahl zw. 0 und 255 in Waitadd. Die Zeitspanne in Takten, welche die Methode nun beim Aufruf wartet, berechnet sich wie folgt: W artetaktanzahl = W aitmul ∗ 255 + W aitadd 21 (3.3) 3.1. DATENÜBERTRAGUNGS-GRUNDLAGEN Im folgenden verwenden wir für die Pegel folgende Variablen: Byte Waitmul Waitadd 1tes Byte 3 128 2tes/3tes Byte 2 128 Daraus ergibt sich für die Gesamtdauer des ersten Bytes t1 : t1 = 8 ∗ Bitdauer = 8 ∗ (2 ∗ W artetaktanzahl ∗ T aktdauer) = 8 ∗ (2 ∗ (W aitmul ∗ 255 + W aitadd) ∗ T aktdauer) (3.4) = 8 ∗ (2 ∗ (3 ∗ 255 + 128) ∗ 109ns) = 1, 56ms Analog ergibt sich die Gesamtdauer eines Datenbytes t2 : t2 = 8 ∗ Bitdauer = ... (3.5) = 8 ∗ (2 ∗ (2 ∗ 255 + 128) ∗ 109ns) = 1, 1ms Daraus errechnet sich die Gesamtpaketdauer tg : tg = t1 + 2 ∗ t2 = 1, 56ms + 2, 2ms = 3, 76ms Damit sind wir in der Lage 1s 3,76ms (3.6) ≈ 265 Pakete je Sekunde zu übertragen. Da wir 2 Byte pro Paket übertragen ergibt sich also insgesamt eine maximale Übertragungsrate von 530 Bytes/s (4,24kBit/s) abzüglich des Fehleranteils. 22 3.2. MODELLIERUNG DER ZEIT: DIE WAIT-METHODE 3.2 Modellierung der Zeit: Die Wait-Methode Die Wait-Methode stellt ein grundlegendes Element für die komplette Software dar. Die Aufgabe, die mit ihr realisiert werden soll, ist das Warten eines bestimmten Zeitintervalls, welches durch den Benutzer vorgegeben werden kann. Die Zeit muss dabei allerdings schon auf Benutzerseite in Taktdauern umgerechnet werden. Die Werte, die man an die Wait-Funktion übergeben muss sind abhängig vom verwendeten Quarz. Auf unseren Versuchs-Boards verwenden wir Quarze mit 9,216 MHz. Daraus ergibt sich die Dauer einer Taktflanke von etwa 109 ns, wie in Kapitel 3.1.3 auf der vorherigen Seite gezeigt. Die Hauptaufgabe der Wait-Funktion ist es, das korrekte Timing für die zu übertragenden Pegel sicher zu stellen. Die von uns verwendeten Funkmodule lassen sich mit bis zu 40 kBit/s ansteuern. Daraus lässt sich nun die minimale Dauer für einen Pegel berechnen. Es ergibt sich ein Wert von 0,025 ms, was etwa 230 Takten entspräche. Die minimale Datenrate, mit der eine korrekte Übertragung sichergestellt ist, beträgt 0,5 kBit/s, was einer maximalen Pegeldauer von 2 ms entspricht. Wieder in Takten gerechnet ergibt sich ein Wert von etwa 18433 Takten. Unsere Zielsetzung war es, möglichst viele Daten mit maximaler Sicherheit zu übertragen. Wir wollten deshalb keiner der beiden Grenzen zu Nahe kommen. Das Problem, welches sich uns nun stellte war, dass wir bei einer Registergröße von acht Bit nur maximal 256 Takt-Dauern warten konnten, wir aber der minimalen Dauer von 230 Takten - wie schon erwähnt - auch nicht zu nahe kommen wollten. Außerdem müssen auch Pegel übertragen werden, die mindestens die doppelte Länge des Basispegels haben, ganz egal welche Codierung wir verwenden. Damit beträgt die minimale Dauer, welche wir mit dieser Funktion warten müssen 460 Takte und damit reicht die Verwendung eines einzelnen Registers nicht mehr aus (außer wir zählen nicht jeden Takt, dazu aber später mehr). Aus diesem Grund mussten wir auf ein weiteres Register zurückgreifen. Die beiden Register sollten zusammen in etwa so funktionieren, wie ein einzelnes 16-Bit-Register. Das erste Register sollte also einzelne Takte zählen, während das zweite Register nach 256 Takten um den Wert eins erhöht werden sollte. Das eigentliche Zählen der Takte übernimmt dabei ein im Controller integrierter 8-Bit Timer, welcher automatisch bei jedem Takt erhöht wird. Bei jedem Überlauf wir ein sog. Überlauf-Flag gesetzt. 23 3.2. MODELLIERUNG DER ZEIT: DIE WAIT-METHODE 3.2.1 Die Funktion im Detail Bei Aufruf der Funktion müssen durch den Benutzer die beiden Register „waitadd“ und „waitmul“ gefüllt sein. In „waitmul“ muss die Anzahl der kompletten 256-Takt-Intervalle stehen, die gewartet werden sollen und in „waitadd“ die einzelnen, restlichen Takte. Bei jedem Überlauf des Timers wird „waitmul“ dekrementiert. Steht in dem Register der Wert Null, so wird der Wert des Timers mit „waitadd“ verglichen. Sobald der Wert im Timer höher ist wird die Funktion verlassen. Der komplette Code: wait: push waitadd push waitmul cpi waitadd,8 brlo wait1 ldi temp,8 sub waitadd,temp ; wir warten einige Takte weniger da durch die Schleifenrückkehr (u.a) ; auch Takte verbraucht werden wait1: cpi waitmul, 0 breq waitrest in temp, tifr sbrs temp,1 rjmp wait1;wenn es keinen überlauf gab geht es woanders weiter dec waitmul ldi temp , (1<<1) out tifr, temp ; das Überlauf-Flag wird auf Null gesetzt rjmp wait1 waitrest: ;jetzt muss noch die zeit von waitadd gewartet werden in temp,tcnt0 cp temp, waitadd brlo waitrest ldi temp, 0 24 3.3. IMPLEMENTIERUNG DER SENDE-ROUTINE out tcnt0, zeroreg pop waitmul pop waitadd ret Es hätten sich auf Basis des verwendeten Controllers noch zwei weitere Möglichkeiten der Realisierung ergeben. Es wäre möglich gewesen den Timer z.B. nur alle 16 oder gar 256 Takte inkrementieren zu lassen. Die Verwendung einer Variante mit großen Zählintervallen hätte unsere Wait-Funktion zwar vereinfacht, ihr aber die Präzision genommen. Die Verwendung eines 8-Takte Intervalls hätte uns das Problem mit dem zweiten Register wohl auch nicht abnehmen können. Die andere Möglichkeit wäre die Verwendung des integrierten 16-Bit-Timers gewesen, welche eine echte Alternative dargestellt hätte. Dieser Timer ist allerdings zusätzlich mit einer optionalen Puls-Weiten-Modulation ausgestattet. Die Verwendung dieser Option wollten wir offen halten und haben deshalb auf den 8-Bit-Timer zurückgegriffen. 3.3 Implementierung der Sende-Routine Die Aufgabe der Sende-Routine ist es Datenpakete auf Grundlage der in Kapitel 3.1.1 auf Seite 17 aufgeführten Übertragungsvorgaben zu verschicken. Es sollen also 16 Bit (zwei Register) asynchron übertragen werden, d.h. es gibt keinen gemeinsamen Takt zwischen den Knoten. Deshalb müssen sich die Knoten zum Anfang eines jeden Paketes synchronisieren. Außerdem gilt es, für die einzelnen Bits die Manchester-Codierung zu verwirklichen. Das Übertragen eines Pegels läuft immer nach dem gleichen Schema ab: Zuerst wird der Pin des Atmels, welcher mit dem Datenpin des Sendemoduls verbunden ist, auf den benötigten Pegel gesetzt und dann die Funktion wait aufgerufen. Die Werte, mit denen wir diese Funktion aufrufen sind in der Sende-Routine fest vorgegeben. Für die Übertragung der Synchronisations-Bits und des Datentypbit verwenden wir für „waitmul“ den Wert 3, für die Datenbits den Wert 2. „waitadd“ beträgt immer 128. Diese Werte sind von uns unter den in Kapitel 3.1.1 auf Seite 17 beschriebenen Voraussetzungen frei gewählt und können auf die jeweiligen Bedürfnisse angepasst werden (die Werte müssen aber mit den in der Empfangs-Routine verwendeten Werten übereinstimmen). 25 3.3. IMPLEMENTIERUNG DER SENDE-ROUTINE Im gleich folgenden Listing ist die Funktion mit Befehls- bzw. Label-Nummern aufgeführt. Die Nummern sollen dem besseren Verständnis dienen, denn auf diese beziehen sich auch die folgenden Erklärungen. Für die Übertragung einzelner Bits nach dem Manchester-Code verwendet die Routine eigene Hilfsroutinen. Eine „0“ wird durch die Routine „zero“ (Nr.:47), eine „1“ durch die Routine „One“ (Nr.: 53) übertragen. Je nach Typ der Bits muss vorher „waitmul“ angepasst werden. Soll beispielsweise eine „0“ übertragen werden, so wird der „Sendepin“ zuerst auf „low“ gesetzt und „wait“ aufgerufen. Danach wird der Pin auf „high“ gesetzt und es wird wiederum gewartet. Soll eine „1“ übertragen werden, so kommt zuerst ein High- und dann ein Low-Pegel. Begonnen wird nun also mit der Übertragung der Synchronisationsbits (Präambel, Nr.: 05). Dazu wird - wie oben beschrieben - „waitmul“ auf 3 gesetzt und es werden sechs Nullen und eine Eins übertragen. Als nächstes wird das Datentypbit übertragen, wofür es auch eine eigene Hilfs-Routine gibt (Nr.: 41). In dieser Routine wird auf unser allgemeines Flag-Register zurückgegriffen, welches vor der Übertragung entsprechend initialisiert sein muss (Datentypbit = 0 für Steuerdaten, Datentypbit = 1 für Nutzdaten, genauere Erläuterungen in Kapitel 3.6 auf Seite 55). Je nach Wert wird dann direkt eine „0“ bzw eine „1“ übertragen. Für die Übertragung der zwei Register („tosend1“ und „tosend2“) benötigen wir noch zwei weitere Register. Zum einen das Register „temp2“, in dem gespeichert steht, welches Register wir gerade übertragen (1 für das niedrigere Byte, also „tosend1“ und 2 für das höhere Byte, also „tosend2“). Außerdem das Register „counter“, welches das als nächstes zu übertragende Bit im Register angibt. Danach machen wir uns eine Kopie des unteren Bytes (Nr. 10) und betreten dann die Übertragungsschleife. Dort wird zuerst überprüft, ob schon acht Bits übertragen wurden (Nr.: 12). Ist dies der Fall, so ist das aktuelle Register komplett und es wird überprüft, ob es sich bereits um das zweite Register gehandelt hat (Nr.: 22), da dann die Routine verlassen werden kann. Wenn es das erste Register war (temp2=1), so muss die Übertragung des zweiten Registers vorbereitet werden (ab Nr.: 27). Führt die Prüfung zu Beginn der Übertragungsschleife nicht zu dem Ergebnis, dass das Register bereits komplett abgearbeitet wurde, so wird als nächstes der Bit-Counter dekrementiert, geprüft welcher Wert übertragen werden muss und dann die entsprechende Funktion aufgerufen (Nr.: 14 bis 18). Danach wird das Register, auf dem wir arbeiten (temp1) nach rechts „geschiftet“, sodass das nächste Bit an erster Stelle steht (für den nächsten Durchlauf)(Nr.: 19). 26 3.3. IMPLEMENTIERUNG DER SENDE-ROUTINE 01:transmit: ; ein Datenpaket wird übertragen (Manchester) ; die zu übertragenden Werte werden in tosend1 ; und tosend2 erwartet 02: cbi portb, enablepin ; Sender aktivieren ; Pegelbreiten der Syn-Pegel: ; Multiplikator: 3, Summand: 128 03: ldi waitmul, 3 04: ldi waitadd, 128 05: rcall preamble ; Übertragen der Präambel 06: rcall databit ; Übertragen des Datentypbit 07: ldi temp2, 1 ; Bytes: 1: unteres Byte wird übertragen ; 0: höheres Byte wird übertagen 08: ldi counter,8 ; 2 mal 8 Bits müssen übertragen werden 09: ldi waitmul, 2 ; Pegelbreiten der Daten: Multiplikator:2, ; Summand: 128 10: mov temp1, tosend1 ; tosend1 als ersten Wert übertragen 11:transloop: 12: cpi counter,0 ; Counter mit Null vergleichen 13: breq transout ; Counter=Null, d.h. es sind 8 Bits übertragen, ; also 2tes Register nachladen oder beendet 14: dec counter 15: sbrc temp1,0 ; Abfrage welcher Wert aus temp1 ; gesendet werden soll 27 3.3. IMPLEMENTIERUNG DER SENDE-ROUTINE 16: rcall one 17: sbrs temp1,0 18: rcall zero 19: lsr temp1 ; nächstes zu sendendes Bit ; in den Carry schieben 20: rjmp transloop 21:transout: 22: sbrc temp2,0 ; muss nachgeladen werden? 23: rjmp secondvalue ; ja: zweiten Wert nach Temp laden ; nein: Sendpin high und Übertragung beenden 24: cbi portd, sendpin 25: sbi portb, enablepin 26: ret ; Sender deaktivieren 27:secondvalue: 28: mov temp1,tosend2 ; 2ten Wert nachladen 29: dec temp2 30: ldi counter,8 ; Counter neu setzen 31: rjmp transloop ; 2tes Byte übertragen ;********************************************** ; Preamble bestehend aus 6xNull und 1xEins ;********************************************** 32:preamble: 33: rcall zero 34: rcall zero 35: rcall zero 36: rcall zero 37: rcall zero 28 3.3. IMPLEMENTIERUNG DER SENDE-ROUTINE 38: rcall zero 39: rcall one 40: ret ;************************************************ ; Datentyp wird übertragen ;************************************************ 41:databit: 42: sbrs flagreg,qdatatype 43: rcall zero 44: sbrc flagreg,qdatatype 45: rcall one 46: ret ;************************************************ ;Übertragung einer Null in Manchester ;************************************************ 47:zero: 48: cbi portd,sendpin ; das Signal wird auf Null gesetzt 49: rcall waitneu ; die Wartezeit wird schon vor zero ; in waitmul geschrieben 50: sbi portd,sendpin ; das Signal wird auf Eins gesetzt 51: rcall waitneu ; der Wert in waitmul darf ; nicht verändert worden sein 52: ret ;************************************************ ;Übertragung einer Eins ;************************************************ 53:one: 54: sbi portd,sendpin ; das Signal wird auf Eins gesetzt 55: rcall waitneu ; die Wartezeit wird schon vor zero ; in wailmul geschrieben 56: cbi portd,sendpin ; das Signal wird auf Null gesetzt 29 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 57: rcall waitneu ; der Wert in waitmul darf ; nicht verändert worden sein 58: 3.4 ret Implementierung der Empfangs-Routine Die Aufgabe der Empfangs-Routine ist es, die Daten, welche mit Hilfe der Sende-Routine aus Kap. 3.3 gesendet wurden, zu empfangen. Dabei sollen aber auch zwei verschiedene Arten von Fehlern erkannt werden: Der Timeout- und der Format-Fehler. Ein Timeout tritt ein, wenn die Routine nicht innerhalb einer gewissen Zeit den Anfang eines Paketes erkannt hat. Ein FormatFehler hingegen liegt dann vor, wenn die nach Manchester erwarteten Pegel nicht gemessen werden. Wird z.B. eine „1“ erwartet, da ein High-Pegel erkannt wurde, so muss darauf ein LowPegel folgen. Im Falle eines Fehlers muss die Empfangs-Routine noch so lange verweilen, wie die Gesamtsendedauer eines intakten Paketes betragen würde. Dieses Warten soll verhindern, dass die Knoten durch direkt darauf folgendes Senden asynchron werden. Außerdem müssen die Flags „Error“ und gegebenenfalls „Timeout“ gesetzt werden. Falls korrekte Werte empfangen wurden, so werden diese in den Registern „sentvalue1“ und „sentvalue2“ bereitgestellt. Der empfangene Datentyp ist dem „datatype-Flag“ zu entnehmen (siehe Kapitel 3.6 auf Seite 55). Da die Routine im Vergleich zur Sende-Routine sehr komplex ist, werden zuerst einige in ihr enthaltene Hilfsroutinen vorgestellt. (Der Quelltext ist ab Seite 33 zu finden und ebenso wie schon bei der Sende-Routine mit Nummern vor jedem Befehl bzw Label versehen, um das Auffinden der Stellen zu erleichtern). So gibt es z.B. zwei Funktionen, welche die Zeit eines Highbzw eines Low-Pegels messen. Sie werden zu Beginn des Pegels gestartet und enden auch mit diesem. Da die beiden Routinen den gleichen Aufbau haben soll hier nur eine von beiden erklärt werden, nämlich die, die einen Low-Pegel misst (Nr.: 131). Die Dauer des Pegels wird direkt in die Register „waitmul“ und „waitadd“ gespeichert (siehe Kap. 3.2). Deshalb werden diese gelöscht und anschließend die eigentliche Schleife betreten. Das Timer-Interrupt-Flag-Register und der 8-Bit-Timer werden ausgelesen (Nr.: 135 und 136). Der Wert des 8-Bit-Timers wird daraufhin dekrementiert, um exakt auf dem gleichen Stand zu sein, wie das Interrupt-Flag. Dann wird getestet, ob das Überlauf-Flag gesetzt war (Nr.: 138), ist dies der Fall, so wird „waitmul“ erhöht und das Flag wieder gelöscht (ab Nr.: 143). Vorher wird allerdings noch getestet, ob das Register schon einen höheren Wert als 8 hat, da in dem Fall kein sinnvolles Ergebnis mehr erreicht 30 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE werden kann. Die Routine wird daraufhin sofort verlassen (Nr.: 140 und 142). Als nächstes wird getestet, ob am Pin immer noch Low-Pegel anliegt (Nr.: 148). Trifft dies zu, so wird die Schleife wieder durchlaufen. Liegt jedoch mittlerweile High-Pegel an, so wird die Schleife nach löschen des Überlauf-Flags (Nr.: 152) verlassen. Wie schon erwähnt funktioniert die Routine, welche HighPegel misst (ab Nr.: 153) bis auf den Test, ob „waitmul“ den Wert 8 überschritten hat, analog zu dieser Routine. Des weiteren wird ab Nr. 63 eine Art Unterprogramm benutzt, um den Inhalt zweier Register zu halbieren, die zusammen ein 16-Bit-Register bilden. Bei den beiden Registern handelt es sich um „waitmul“ und „waitadd“. Zuerst wird geprüft, ob das unterste Bit im oberen Register („waitmul“) gesetzt ist. Ist dies der Fall, so wird das untere Register nach rechts geschoben und mit 128 addiert. Ist dies nicht der Fall, so wird auf die Addition verzichtet. Das obere Register wird ebenfalls nach rechts geschoben. Alternativ dazu hätte man auch einfach das obere Register nach rechts in den Carry schieben und das untere Register durch den Carry nach rechts rotieren können. Es folgen zwei „Unterprogramme“ welche für die Einhaltung der Manchester-Codierung Sorge tragen. Es handelt sich dabei um „zeroexpected“ und „oneexpected“. Da wieder beide analog funktionieren, wird an dieser Stelle nur „zeroexpected“ vorgestellt (ab Nr.: 102). Die Ausgangslage ist folgende: Die Hauptschleife zur Erkennung der Bits hat einen Low-Pegel erkannt und wir befinden uns zeitlich gesehen gerade in der Mitte dieses Pegels. Nach dem Manchester-Code erwarten wir nun einen High-Pegel. Die Register für die wait-Routine sind so initialisiert, dass eine halbe Pegeldauer gewartet wird. Es wird direkt bis zum Ende des Pegels gewartet und die Werte für wait werden sicherheitshalber erneut geladen. Mit dem nächsten wait (Nr.: 107) springen wir in die Mitte des nächsten Pegels. Jetzt wird getestet, ob dieser auch wirklich High ist (Nr.: 108). Ist dies nicht der Fall liegt ein Fehler vor, der bei Nr. 119 behandelt wird. Liegt jedoch der erwartete High-Pegel an, so wird „carry“ gelöscht und das Ergebnisregister durch den „carry“ rotiert (Nr.: 110 und 111), was einem „Hineinschieben“ einer 0 entspricht. Nun zum Ablauf der Haupt-Empfangs-Routine. Zuerst werden die Timer gelöscht und das Flagregister initialisiert. Nun wird die Hauptschleife betreten, in der auf den Anfang eines Paketes gewartet werden soll. Es wird geprüft, ob das Flag „initwait“ gesetzt ist (Nr.: 8). Das setzen dieses Flags hat zur Folge, dass keine Timeouts eintreten können, oder anders gesagt: Die Empfangs- 31 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE Routine wartet dann ewig auf ein Signal. Verwendet wird dies vom passiven Knoten, während dieser darauf wartet, dass der aktive Knoten die Verbindung aufbaut. Ist das Flag nicht gesetzt, so wird jetzt auf einen Timeout geprüft. Dazu wird der 16-Bit-Timer eingelesen und das obere Byte mit dem Wert 28 verglichen (ab Nr.: 10). Wurde der Wert überschritten, so wird „Timeout“ gesetzt und die Prozedur nach einer Warte-Schleife und nach setzen des „error-Flags“ verlassen (ab Nr.: 119). Kam es nicht zum Timeout, so wird geprüft, ob Low-Pegel anliegt. Wir erwarten, dass jedes Paket mit einem Low-Pegel beginnt (Nr.: 17) (der Paketaufbau ist in Abb. 3.2 auf Seite 19 zu sehen). Liegt High-Pegel an, so wird die Schleife erneut durchlaufen. Bei Low-Pegel löschen wir den 8-Bit-Timer und das Überlauf-Flag. Wir erwarten nun die Präambel. Dazu messen wir in Folge Low-, High- und wieder Low-Pegel (ab Nr.: 26). Liegt einer dieser Pegel nicht in dem von uns erwarteten Bereich für Synchronisations-Pegel, so springen wir direkt zurück in die Schleife, die auf den Beginn des Paketes wartet. Andernfalls gehen wir einen Schritt weiter und warten jetzt auf einen doppelten High-Pegel (ab Nr.: 46), der den eigentlichen Anfang des Paketes einleitet. Vorher wird nochmals auf einen Timeout überprüft (ab Nr.: 40), sofern „initwait“ nicht gesetzt ist. Liegt nun High-Pegel an löschen wir den 8-Bit-Timer und messen diesen Pegel. Wir erwarten - wie erwähnt - einen doppelten Pegel. Da ein Pegel „3,5 waits“ lang ist erwarten wir jetzt ein „waitmul“ von 6 oder 7. Das liegt an gewissen Messungenauigkeiten. Möglich sind z.B. „waitmul“ gleich 6 und „waitadd“ nahe 255 oder „waitmul“ gleich 7 und „waitadd“ nahe 0. Ergibt sich keiner dieser Werte, so warten wir auf den nächsten High-Pegel. Ist der Wert jedoch richtig gemessen, so erwarten wir als nächstes das Datentypbit. Vorher initialisieren wir die Register der wait-Routine mit den Werten für Synchronisationsbits und halbieren diese, wie oben beschrieben. Mit den so gewonnenen Werten rufen wir wait auf und stehen jetzt in der Mitte des ersten Pegels des Datentyp-Bits (Nr.: 73). Je nach gemessenem Wert wird jetzt das Datentyp-Flag gesetzt und die Dauer von 1,5 Pegeln abgewartet. Damit sollten wir uns jetzt genau zwischen dem Overhead-Byte und den Payload-Bytes befinden. (Nr.: 80). Die wait-Routine wird nun mit den Werten für die Nutzlast geladen. Das Register counter soll die einzelnen empfangenen Bits herunter zählen und wird daher mit dem Wert 16 initialisiert. Nun beginnt die Schleife, welche die einzelnen Bits erkennen soll. Wir warten bis zur Mitte des aktuellen Pegels. Messen wir einen High-Pegel, so erwarten wir eine 1, messen wir hingegen einen Low-Pegel, so erwarten wir eine 0. Die jeweilige Funktion dafür wird aufgerufen (siehe Erläuterung weiter oben). Das Ergebnis wird in das Register „sentvalue2“ geschoben. Nun wird geprüft, 32 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE ob counter den Wert 8 hat (Nr.: 115). Dies würde bedeuten, dass das erste Register komplett empfangen ist. Wir würden den Wert aus „sentvalue2“ nach „sentvalue1“ schreiben und weiter mit der Schleife fortfahren, d.h. die nächsten 8 Bits empfangen und in das Register „sentvalue2“ schreiben. Sobald ein Test ergibt, dass wir alle Bits empfangen haben (Nr.: 85), verlassen wir die Routine mit den Empfangenen Werten in „sentvalue1“ und „sentvalue2“. Hier nun der Code: 01:receive: ;###### 02: out tcnt0, zeroreg 03: out tcnt1h, zeroreg 04: out tcnt1l, zeroreg ; die Counter werden gelöscht ;###### ;###### 05: cbr flagreg, error 06: cbr flagreg, timeout 07: cbr flagreg, datatype ; das flagreg wird initialisiert ;###### 07:receivelabel1: 08: sbrc flagreg, qinitwait ; ist das initwait-bit gesetzt wird nicht auf ; "timeout" überprüft 09: rjmp receivelabel2 ;###### 10: in temp,tcnt1l 11: in temp, tcnt1h 12: cpi temp,28 13: brlo receivelabel2 14: sbr flagreg, timeout ; prüfen auf Timeout ; obere Byte von Counter2 5 oder höher =>Timeout 33 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 15: rjmp receiveerror ;###### 16:receivelabel2: 17: sbic pinb,pinb0 18: rjmp receivelabel1 19: ;###### 20: out tcnt0,zeroreg 21: ldi temp , (1<<1) 22: out tifr, temp 23: ;###### 24: ldi counter,0 ; wenn pin0 auf NULL, dann kann gemessen werden ; Timer0 wird eingestellt ; das Überlauf-Flag wird auf Null gesetzt 25:receivepreamble1: 26: rcall measurelow 27: cp waitmul, synpegel 28: brne receivelabel1 29: inc counter 30: cpi counter, 3 31: breq receivepreamble2 32: rcall measurehigh 33: cp waitmul, synpegel 34: brne receivelabel1 35: inc counter 36: rjmp receivepreamble1 34 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 37:receivepreamble2: ; jetzt muss auf den Anfang der NutzDaten ; (also Datentyp und Daten) gewartet werden 38: sbrc flagreg, qinitwait ; ist initwait gesetzt so muss nicht ; auf timeout geprüft werden 39: rjmp receivepreamble3 ;###### 40: in temp,tcnt1l 41: in temp, tcnt1h 42: cpi temp,52 43: brlo receivepreamble3 44: sbr flagreg, timeout 45: rjmp receiveerror ; auf Timeout prüfen ; obere Byte von Counter2 33 oder höher =>Timeout ;###### 46:receivepreamble3: 47: sbis pinb,pinb0 48: rjmp receivepreamble2 ; das Signal liegt jetzt an 49: out tcnt0, zeroreg 50: rcall measurehigh 51: ;###### ; Pegellänge überprüfen (6 oder 7 wird erwartet) 52: cpi waitmul, 7 ; doppelter syn 53: breq receivegoon 54: cpi waitmul, 6 55: breq receivegoon 56: ;###### ; doppelter syn 35 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 57: rjmp receivepreamble2 58:receivegoon: 59: out 60: mov waitmul, synpegel 61: mov waitadd, synpegeladd 62: rcall wait ; der Anfangspegel wurde richtig erkannt tcnt0,zeroreg ;####### 63: sbrs waitmul, 0 64: rjmp receivelabel3 ; halbieren der Werte ; wenn Bit0 1 ist dann war der Wert ; ungerade und es müssen 128 ; Takte gewartet werden bei label 5 geht es ; weiter wenn die Zahl gerade war 65: ldi temp, 128 66: lsr waitadd 67: add waitadd, temp 68: rjmp receivelabel4 ; die Werte sind jetzt korrekt und ; es kann fortgefahren werden 69:receivelabel3: 70: lsr waitadd 71:receivelabel4: 72: lsr waitmul ; die Werte in waitadd und waitmul entsprechen jetzt ; der hälfte einer Signaldauer ; der counter läuft währenddessen weiter ;####### ; halbieren der Werte fertig _________________ 36 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 73: rcall wait ; jetzt kommt das Datentypbit 74: cbr flagreg,datatype 75: sbic pinb, pinb0 ; liegt eine Null an pinb0 an wird ; der nächste Befehl übersprungen 76: sbr flagreg,datatype ;####### 77: rcall wait 78: rcall wait 79: rcall wait ; 1,5 Pegel warten ;####### 80: ldi waitmul, datapegel 81: ldi waitadd, datapegeladd ; jetzt kommen die 16 Datenbits 82: ldi counter, 16 ; wir stehen jetzt auf dem ersten ; Pegel des ersten Bits 83: rcall wait 84:measureloop: 85: cpi counter,0 ; 16 Werte müssen ermittelt werden, ; das niederwertigste ; Bit wird zuerst übertragen 86: breq measureout 37 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 87: dec counter 88: sbis pinb, pinb0 ; liegt eine Eins an pinb0 an wird der ; nächste Befehl übersprungen 89: rjmp zeroexpected 90:oneexpected: 91: rcall wait 92: ldi waitadd, datapegeladd 93: ldi waitmul, datapegel 94: out tcnt0, zeroreg 95: ;ldi temp, 0x31 96: rcall wait ; auf die zweite hälfte des Signals warten 97: sbic pinb,pinb0 ; Skip if Bit in Io is Cleared 98: rjmp receiveerror ; wenn jetzt eine Eins anliegt ; ist ein Fehler aufgetreten 99: sec ; carry wird gesetzt 100: ror sentvalue2 ; sentvalue wird über den Carry rotiert 101: rjmp prepareformeasureloop 102:zeroexpected: 103: rcall wait 104: ldi waitadd, datapegeladd 105: ldi waitmul, datapegel 106: out tcnt0, zeroreg 107: rcall wait 108: sbis pinb,pinb0 109: rjmp receiveerror 110: clc 111: ror sentvalue2 112:prepareformeasureloop: 113: rcall wait 114: rcall wait ; die Messung des nächsten Bits wird vorbereitet 38 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 115: cpi counter , 8 116: brne measureloop 117: mov sentvalue1, sentvalue2 118: rjmp measureloop 119:receiveerror: ; jeder "gemessene" Fehler führt zu diesem Label 120: sbrc flagreg, qinitwait ; ist initwait gesetzt so wird receive direkt beendet 121: ret ;###### 122: in temp,tcnt1l 123: in temp, tcnt1h 124: cpi temp,136 125: brlo receiveerror ; Ende eines ganzen Paketes abwarten ;###### ;###### Rücksetzen des Timers 126: out tcnt1h, zeroreg 127: out tcnt1l, zeroreg ;###### 128: sbr flagreg, error ; Fehler-Flag wird gesetzt 129:measureout: 130: ret ;*************************************** ende receive************************ ;**************************** anfang measurelow ***************************** ; measurelow misst die Länge eines 0-Pegels. ; Vielfache von 255 werden in Counter ; gespeichert, der Rest der Takte steht in r17 131:measurelow: 132: clr waitmul 133: clr waitadd 134:measurelow1: 39 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 135: in temp, tifr 136: in waitadd, tcnt0 137: dec waitadd 138: sbrs temp,1 139: rjmp measurelow3 140: cpi waitmul,8 ; gab es einen Überlauf? ; überschreitet die Pegellänge 8 ; so ist der Pegel fehlerhaft 141: brne measurelow2 142: ret 143:measurelow2: 144: inc waitmul 145: ldi temp , (1<<1) 146: out tifr, temp ; in waitmul werden die Überläufe gezählt ; das Überlauf-Flag wird wieder auf Null gesetzt 147:measurelow3: 148: sbis pinb, pinb0 149: rjmp measurelow1 ; liegt weiter Eins an, so kann der Timer einfach ; weiter laufen wenn nicht ist jetzt in Counter ; die Anzahl der Überläufe gespeichert 150: out tcnt0,zeroreg ; der Timer wird direkt wieder auf 0 gesetzt ; um möglichst wenig Takte zu verlieren 151: ldi temp , (1<<1) 152: out tifr, temp 152: ret ; das Überlauf-Flag wird wieder auf Null gesetzt ;******************************* ende measurelow ************************* ;******************************** anfang measurehigh ************************* ; measurehigh misst die Länge eines 0-Pegels. ; Vielfache von 255 werden in Counter ; gespeichert, der Rest der Takte steht in r17 40 3.4. IMPLEMENTIERUNG DER EMPFANGS-ROUTINE 153:measurehigh: 154: clr waitmul 155: clr waitadd 156:measurehigh1: 157: in temp, tifr 158: in waitadd, tcnt0 159: dec waitadd 160: sbrs temp,1 161: rjmp measurehigh2 162: inc waitmul 163: ldi temp , (1<<1) 164: out tifr, temp ; in Counter werden die Überläufe gezählt ; das Überlauf-Flag wird wieder auf Null gesetzt 165:measurehigh2: 166: sbic pinb, pinb0 167: rjmp measurehigh1 ; liegt weiter Null an, so kann der Timer einfach ; weiter laufen wenn nicht ist jetzt in Counter ; die Anzahl der Überläufe gespeichert 168: out tcnt0,zeroreg ; der Timer wird direkt wieder auf 0 gesetzt ; um möglichst wenig Takte zu verlieren 169: ldi temp , (1<<1) 170: out tifr, temp 171: ret ; das Überlauf-Flag wird wieder auf Null gesetzt 41 3.5. ÜBERTRAGUNGS-PROTOKOLL 3.5 Übertragungs-Protokoll Als Grundlage für die meisten Netzwerkprotokolle dient das seit 1979 entwickelte und standardisierte OSI-Modell. Es besteht aus sieben Schichten, von denen jede Schicht für bestimmte Aufgaben zuständig ist. Da unsere Aufgabe nur darin bestand, eine Funkübertragung mit Controllern zu realisieren, entschieden wir uns für ein minimales Protokoll. In unserem Fall wird das „OSI-Modell“ deshalb auf drei Schichten reduziert. Viele der Aufgaben (vor allem der höheren Schichten) werden von unserem Protokoll nicht komplett oder gar nicht berücksichtigt. Die unterste Schicht ist in unserem Fall eine rein physikalische Schicht, welche durch die Funkmodule übernommen wird. Diese sind ausschliesslich für das Übertragen von Pegeln zuständig. Die nächste Schicht wird durch die Sende- und Empfangsroutine definiert. Sie legt die Darstellung eines Bits und den Paketaufbau fest. Sie ist außerdem für die Erkennung von Fehlern im Paket zuständig. Die dritte Schicht soll nun Bestandteil dieses Anschnittes sein. Zum einen soll eine Verbindung zwischen den Knoten hergestellt und aufrecht erhalten werden, in der die Knoten abwechselnd Daten übertragen. Zum anderen soll die Verbindung bei einer zu großen Fehlerzahl abgebrochen und ein neuer Verbindungsversuch gestartet werden. Die Verbindung soll von einem Knoten aus initiiert werden und dann von diesem auf Wunsch auch wieder getrennt werden können. Wir unterscheiden deshalb zwischen dem aktiven Knoten (siehe Abb. 3.4 auf der nächsten Seite) und dem passiven Knoten (siehe Abb. 3.5 auf Seite 50). 42 3.5. ÜBERTRAGUNGS-PROTOKOLL 3.5.1 Der aktive Knoten Abbildung 3.4: Aktiver Knoten Beim aktiven Knoten unterscheiden wir vier verschiedene Zustände. Im Startzustand „0“ wartet der Knoten darauf, dass er die Verbindung auf Benutzerwunsch starten soll. Ausgelöst wird dies in unserem Fall durch den Interrupt „INT0“, der wiederum durch Drücken eines Tasters ausgelöst wird. Daraufhin sendet der Knoten ein „syn-Paket“ und wechselt in den HandshakeZustand „1“. Von dort aus gibt es drei Übergänge, wovon zwei durch Fehler eingeleitet werden. Wir unterscheiden zwei „Gruppen“ von Fehlern, welche wir fehleraktiv und fehlerpassiv nennen. Das hat nichts mit der Art des Fehlers an sich zu tun, also ob es sich um einen Timeout oder einen Format-Fehler handelt. Fehleraktiv bedeutet, dass die Verbindung nach diesem Fehler weiter bestehen bleiben soll. Fehlerpassiv bedeutet, dass der Knoten nach Eintreten des Fehlers in den Passiven Zustand vor dem Verbindungsaufbau zurück fällt. Passiv deswegen, weil in diesem Zustand wieder auf den Aufbau der Verbindung gewartet wird. Für die Unterscheidung der beiden Fehler-Arten gibt es einen Fehler-Zähler. Übersteigt die Fehlerzahl einen vorher festgelegten Schwellenwert, so bedeutet dies einen „passiven“ Fehler. Das Ereignis ERRORAKTIVE zieht ein erneutes Senden des syn-Paketes nach sich. Es gibt keinen Übergang in einen anderen Zustand. Tritt das Ereignis ERRORPASSIVE ein, so fällt der Knoten ohne weitere Ausgabe in den Zustand „0“ zurück. Empfängt der Knoten das erwartete „synack-Paket“, so geht er unter Senden 43 3.5. ÜBERTRAGUNGS-PROTOKOLL des ersten Datenpaketes in den Interaktions-Zustand „2“ über. Von dort aus gibt es insgesamt vier Übergänge. Auch hier gibt es die Ereignisse ERRORAKTIVE und ERRORPASSIVE. Bei ERRORAKTIVE wird nur der Fehler gezählt, die Verbindung bleibt im gleichen Zustand wie vorher und es werden weiter Daten geschickt. ERRORPASSIVE führt hingegen zu einem Abbruch der Nutzdatenübertragung. Der Knoten fällt unter erneutem Senden des „syn-Paketes“ zurück in den Handshake-Zustand „1“. Empfängt der Knoten ein korrektes Datum vom anderen Knoten (rdata), so ändert sich der Zustand nicht und es wird das nächste eigene Datum geschickt (sdata). Das Eintreten von INT0 signalisiert, dass die Verbindung abgebaut werden soll. Der Knoten reagiert darauf mit Senden eines „fin-Pakets“. Er geht über in den Verbindungsabbau-Zustand „3“. Vom Zustand „3“ führen alle Wege zurück in den Startzustand „0“. Der Knoten wird also auf jeden Fall nach INT0 eine bestehende Verbindung beenden. Der nun folgende Code beinhaltet das eben beschriebene Protokoll. Es beginnt beim Label „endless2“, einer Endlosschleife, die verlassen wird, sobald durch den Interrupt INT0 ein „connectFlag“ gesetzt wird. endless2: sbrs flagreg, qconnect ;wenn das "flag" connect gesetzt ist: ;Sprung zu handshake rjmp endless2 rjmp handshake ;+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+ ; Protokoll Handshake: cbr flagreg, initwait ;schickt das erste paket und ;braucht daher kein initwait sbi portd, gelb ;gelbe LED an cbi portd, gruen ;grüne LED aus 44 3.5. ÜBERTRAGUNGS-PROTOKOLL cbi portd, rot ;rote LED aus clr errorcount sendsyn: ;######## ;der erste Wert wird übertragen ldi tosend1,0x01 ;Empfänger erwartet 0,1,1 ;(datatype,value1,value2) ldi tosend2,0x01 cbr flagreg, datatype rcall transmit sbi portd, sendpin ;######## clr sentvalue1 ;######## ;der erste Wert der Gegenseite ;wird empfangen und geprüft rcall receive ;wir erwarten SYNACK von der Steuerung sbrc flagreg,qerror ;Error? rjmp handshakeerror sbrc flagreg,qtimeout ;Timeout? rjmp handshakeerror sbrc flagreg, qdatatype ;Datentyp ok? rjmp handshakeerror ;ansonsten zurück und warten cpi sentvalue1, 0xff brne handshakeerror ;####### 45 3.5. ÜBERTRAGUNGS-PROTOKOLL ;Handshake zu Ende ;####### ;Interrupt int0 wird reinitialisiert ldi temp, (1<<6) out gifr, temp out gimsk, temp ;ext_int0 anschalten ;####### ldi tosend1, 0x00 ldi tosend2, 0x00 sbr flagreg,datatype ;Nutzdaten haben den Typ "1" rjmp interaction handshakeerror: inc errorcount cpi errorcount, maxerrorhandshake brlo sendsyn ;es wird erneut versucht die Verbindung herzustellen ;####### ;Wiederherstellung des Ausgangszustandes ;(Fehlerzahl zu hoch) cbr flagreg, connect ;"connect-Flag" wird wieder gelöscht ldi temp, (1<<6) out gifr, temp ldi temp, (1<<6) out gimsk, temp ;ext_int0 anschalten ;####### rjmp initial interaction: ;Hauptschleife der Datenübertragung 46 3.5. ÜBERTRAGUNGS-PROTOKOLL ;######### ;gezählte Fehler ausgeben cpi tosend1, 15 brne messflag1 out udr,messcounter ldi messcounter,0 ;######### messflag1: sbi portd, gruen cbi portd, gelb cbi portd, rot sbrs flagreg, qconnect ;"connect-Flag" = 0 => Verbindung abbauen rjmp endtransmission ;######## ;An dieser Stelle müssen die Werte in die Register tosend1 und tosend2 ;geschrieben werden ;######## ;####### ;aktueller Wert wird übertragen rcall transmit cbi portd,sendpin ;####### ;####### ;Wert wird empfangen und überprüft clr sentvalue1 cbr flagreg, datatype rcall receive ;nächster Wert wird empfangen sbrc flagreg,qerror ;Error? erwartet: Bit0 = 0 rjmp errorcount 47 3.5. ÜBERTRAGUNGS-PROTOKOLL sbrs flagreg, qdatatype ;Datentyp ok? erwartet: Typ 0 rjmp errorcount ;ansonsten zurück und warten ldi errorcount, 0 ;####### ;######## ;Die empfangenen Werte können an dieser Stelle ausgewertet werden ;######## rjmp interaction errorcount: inc messcounter inc errorcount cpi errorcount, maxerror breq ende rjmp interaction endtransmission: ;die Übertragung wird nach ;drücken des Buttons beendet ;####### ;Der Empfänger erwartet (0,2,2) cbr flagreg, datatype ldi tosend1,0x02 ldi tosend2,0x02 rcall transmit ;####### rcall receive ldi temp, (1<<6) 48 3.5. ÜBERTRAGUNGS-PROTOKOLL out gimsk, temp ;ext_int0 anschalten rjmp initial ende: ;die Verbindung ist unerwartet abgebrochen ldi waitmul, 255 ldi errorcount,141 ende1: ;####### ;etwa eine Sekunde warten cpi errorcount,0 breq ende2 rcall wait dec errorcount rjmp ende1 ;####### ende2: clr errorcount rjmp handshake 49 3.5. ÜBERTRAGUNGS-PROTOKOLL 3.5.2 Der passive Knoten Abbildung 3.5: Passiver Knoten Der passive Knoten hat - eben bedingt durch seine Passivität - zwei Zustände weniger. Im Startzustand „0“ wartet er auf ein „syn-Paket“. Bekommt er dieses so geht er in den InteraktionsZustand „1“ über, wobei er ein „finack-Paket“ ausgibt. Jedes andere Ereignis wird als ERROR gewertet und ändert den Zustand nicht. Im Zustand „1“ gibt es fünf Übergänge. Darunter wieder zwei, welche der Fehlerbehandlung dienen (siehe Kap. 3.5.1). Bei ERRORAKTIVE wird nur der Fehler gezählt und es wird das nächste Datum übertragen (sdata). ERRORPASSIVE führt zurück in den Startzustand „0“. Es gibt keine Ausgabe. Wird ein korrektes Datum empfangen (rdata), so wird das nächste Datum gesendet (sdata) und es gibt keinen Übergang. Wird erneut das „syn-Paket“ empfangen, so lautet die Antwort darauf wieder synack. Der Zustand ändert sich auch hier nicht. Empfängt der Knoten ein „fin-Paket“, so kehrt er unter Ausgabe von finack zurück in den Startzustand. Nun folgt wieder der entsprechende Quelltext, beginnend mit dem Aufruf der Initialisierungen (Erläuterungen hierzu siehe 3.6 auf Seite 55). Dort werden z.B. Timer und der Stackpointer initialisiert. Danach wird das Label „waitforconnect“ betreten, was dem Startzustand „0“ entspricht. ;+#+#+#+#+#+#+#+#+#+#+#+#+#+#++#+#+#+#+#+#+#+#+#+#+ ; Hauptprogramm der Steuerung Handshake: rcall initial ; Initialisierungen waitforconnect: sbi portb, enablepin ; Sicherstellen, dass der Sender inaktiv 50 3.5. ÜBERTRAGUNGS-PROTOKOLL clr errors ; Übertagungsfehler-Zähler clr errorcount ; *** Teil der Ausgabe zur Fehlerüberwachung *** cbi portd, redled ; rote cbi portd, greenled ; grüne LED aus sbi portd, yellowled ; gelbe LED zeigt an -> warten auf erstes Paket clr sentvalue1 ; Register für empfangen Werte leeren LED aus clr sentvalue2 cbr flagreg, datatype ; alle Bits des Flag-Registers Nullsetzen cbr flagreg, timeout cbr flagreg, error sbr flagreg, initwait ; Initwait Bit->1 ; 1: warten auf erstes Paket, Timeout inaktiv ; 0: erstes Paket erhalten, Timeouts aktiv rcall receive ; warten auf Anmeldung cbr flagreg, initwait ; Initwait Bit->0 / timeouts nun aktiv sbrc flagreg,qerror ; Übertragungsfehler? 0 -> alles ok rjmp waitforconnect ; 1 -> erneute Anmeldung erforderlich sbrc flagreg, qdatatype ; Datentyp ok? 1 -> Protokoll-Daten rjmp waitforconnect ; 0 -> Datenpaket cpi sentvalue1, syn ; Syn gemeldet? brne waitforconnect 51 3.5. ÜBERTRAGUNGS-PROTOKOLL ; Syn erfolgreich erhalten, antworten mit SynAck; synackagain: cbr flagreg, datatype ; Datentyp auf Null setzen ldi tosend1, synack ; SYNACK laden ldi tosend2, synack ; SYNACK laden rcall transmit ; SYNACK senden interaction: cbi portd, redled ; rote LED aus sbi portd, greenled ; grüne LED an -> Interaktionsphase cbi portd, yellowled ; gelbe LED aus clr sentvalue1 ; Register rücksetzen clr sentvalue2 cbr flagreg, datatype ; " cbr flagreg, timeout ; " cbr flagreg, error ; " rcall receive ; neue Daten empfangen sbrc flagreg,qerror ; Error? Ja, Fehlerzähler hochsetzen rjmp errorcount sbrs flagreg, qdatatype ; Datentyp? 0-> Datenpaket ; 1-> Protokollpaket -> Verbindungabbau oder ; Syn nochmal erhalten rjmp endinteraction clr errorcount ; Fehlerzähler rücksetzen, 52 3.5. ÜBERTRAGUNGS-PROTOKOLL ; nur Folgefehler führen zum Abbruch ; erhaltene Daten sind korrekt und können verarbeitet werden ;out udr, sentvalue1 ; Ausgabe der erhaltenen Werte hier möglich ;out udr, sentvalue2 ; Achtung: immer nur einen Wert, da UART nicht schnell genug sendeanweisung: rcall neuewerte ; laden der zu übertragenden Werte rcall transmit ; Übertragung rjmp interaction ; Zyklus wiederholen errorcount: inc errors ; Zähler zur Fehler-Auswertung inc errorcount ; Fehlerzähler hochsetzen cpi errorcount, maxerror ; MaxError erreicht? ja->Verbindung abgebrochen breq maxfehlererreicht rjmp sendeanweisung ; ansonsten neue Werte übertragen maxfehlererreicht: ; Verbingung beenden, auf neue Anmeldung warten ;ldi temp, 0xCD ; Kontrollausgabe ;out udr, temp rjmp waitforconnect ; neuer Zyklus endinteraction: cpi sentvalue1, syn ; es könnte sich auch um einen neuen 53 3.5. ÜBERTRAGUNGS-PROTOKOLL breq synackagain ; Anmeldungsversuch handeln cpi sentvalue1, fin ; Datentyp war Null, wir erwarten also ; das FIN-Flag, nun testen ob es auch stimmt breq sendFinAck ; Ja, FIN richtig erhalten, wir senden FINACK rjmp sendeanweisung ; FIN war falsch, zurück und neue Anweisungen sendFinAck: ldi tosend1, finack ; FINACK laden ldi tosend2, finack rcall transmit ; Übertragung sbi portd, sendpin ; Sendpin auf High warten ;ldi temp, 0xcc ; Kontrollausgabe ;out udr, temp ende: rjmp waitforconnect ; neuer Zyklus neuewerte: ; Lädt die zu übertragenden Werte in tosend1 und tosend2 ; zum Test: wir zählen die Werte von 0 bis ff hoch inc tosend1 inc tosend2 cpi tosend1, 0x00 ; erreichen wir wieder den Wert 0 so wird ; Auswertungsfehlerzähler rückgesetzt ; da wir die Fehler auf 256-Pakete messen breq reset 54 3.6. GRUNDGERÜST DES PROGRAMMS cpi tosend1,0xFF ; FF Pakete erreicht -> Fehleranzahl ausgeben breq fehlerausgabe ret fehlerausgabe: out udr, errors ; Fehleranzahl ausgeben ret reset: clr errors ; Fehler rücksetzen ret ; Ende Hauptprogramm ;+#+#+#+#+#+#+#+#+#+#+#+#+#+#++#+#+#+#+#+#+#+#+#+#+ 3.6 Grundgerüst des Programms Unsere Programme beginnen mit einigen Definition, welche uns im weiteren Verlauf zur besseren Lesbarkeit und höherer Flexibilität verhalfen: .NOLIST .INCLUDE "C:\Programme\Atmel\AVR Studio4\AvrAssembler\Appnotes\2313def.inc" .LIST .def zeroreg=r0 .def synpegel=r1 .def synpegeladd=r2 .def datapegel = r3 .def datapegeladd= r4 .def temp=r16 .def temp1=r17 .def temp2=r18 .def tosend1=r19 .def tosend2=r20 .def messcounter=r21 55 3.6. GRUNDGERÜST DES PROGRAMMS .def sentvalue1=r23 .def sentvalue2=r24 .def counter=r25 .def flagreg=r26 ;|always 1 (interrupt)|n|n|initwait|connect/disconnect|datatype|timeout|error| .def bitcounter=r27 .def errorcount=r28 .def counter2=r29 .def waitmul=r30 .def waitadd=r31 .equ qconnect = 3 .equ connect= 8 .equ datatype= 4 .equ qdatatype=2 .equ timeout = 2 .equ qtimeout = 1 .equ error = 1 .equ qerror =0 .equ initwait =16 .equ qinitwait=4 .equ maxerror= 25 .equ maxerrorhandshake=20 .equ maxtimeout= 200 .equ sendpin = 6 .equ enablepin=1 .equ gruen = 5 .equ gelb = 4 .equ rot =3 .equ datapegeladd=64 .equ datapegel=1 .equ datapegelmess=2 So können wir beispielsweise Registern Klartext-Namen geben. Das geschieht mit „.def“ - Befehlen. Des weiteren können wir mit „.equ“ - Befehlen Werte definieren, welche wir später benötigen. 56 3.6. GRUNDGERÜST DES PROGRAMMS In der Software, welche wir zur Programmierung verwendeten (siehe Kap. 2.5), war die Datei „2313def.inc“ enthalten, welche ebenfalls solche Definitionen vorgibt. Diese Datei muss mit dem „.include“-Befehl eingebunden werden. Ein besonderes Augenmerk sollte dem „flagreg“ zukommen. Dieses Register spielt eine entscheidende Rolle in unserem Programm. Wie der Name schon sagt, werden in ihm eigene durch uns definierte Flags gespeichert: always 1 (interrupt) Wird für den Interrupt INT0 gebraucht (wird später noch erklärt) initwait Wird für die Empfangs-Routine benötigt und sorgt dafür, dass länger auf den Beginn eines Paketes gewartet wird (siehe Kap. 3.4) connect\disconnect Dieses Flag wird gesetzt, wenn die Verbindung aufgebaut werden soll und wieder gelöscht, wenn sie getrennt werden soll datatype Wird gesetzt, falls es sich beim gesendeten oder beim zu sendendem Paket um Nutzdaten und nicht um Steuerdaten handelt timeout Zeigt das Eintreten eines Timeouts an error Wird im Falle eines Fehlers gesetzt Als nächstes folgen im Programm die Sprungadressen für Interrupts. Wird ein solcher ausgelöst, so wird für diesen eine ganz bestimmte Befehlszeile ausgeführt. Bei einem Reset ist dies die Adresse 0x00, bei INT0 0x01 usw. Wir benötigen nur die ersten beiden Interrupts, deswegen kehren wir bei jedem anderen Interrupt mit „reti“ sofort zur Programmausführung zurück: 57 3.6. GRUNDGERÜST DES PROGRAMMS .org 0x000 ;######## Sprungadressen bei Interrupts rjmp initial rjmp EXT_INT0 reti reti reti reti reti reti reti reti reti ;######## Als nächstes folgen einige Initialisierungen. Dazu gehört z.B. die Definition der Eingabe- und Ausgabeports, die Vorbelegung einiger Register oder die Initialisierung des Stacks: initial: ldi temp, 0 ; 0 in Zeroreg mov zeroreg,temp out timsk,zeroreg ; Timer counter interrupt mask beinhaltet overflow-enables clr flagreg ;########## ; anfang Watchdog ausschalten ldi temp,$1f ;out udr, temp OUT WDTCR,temp LDI temp,$17 OUT WDTCR,temp ;########## ; Ende Watchdog ausschalten sei ; globes interruptenable 58 3.6. GRUNDGERÜST DES PROGRAMMS ;########## ; Interrupt int0 wird auf "falling edge" ; gestellt und aktiviert ldi temp, 0b00000010 out mcucr, temp ldi temp, (1<<6) out gimsk, temp ; ext_int0 anschalten ;########## ; Interrupt int0 bereit ;########## ; initialisierung des Stackpointers ldi temp1, 0xdf ; Stackpointer out SPL, temp1 ; " " ;########## ;########## ; Ports werden für Ein/Ausgabe definiert ldi temp1, 0b01111100 out DDRD, temp1 ; Port D wird zur Ausgabe (pin2,3,4,5,6) cbi DDRB, pinb0 ; pinb 0 als Eingabe sbi ddrb, enablepin;pinb1 als Ausgabe ;########## ;########## ; Timer 0 und 1 wird eingerichtet ; und das Überlauf Flag gelöscht (nur 0) ldi temp1, 1 ; Timer auf 1/1 Takte stellen out tccr0,temp1 out tccr1b, temp1 ldi temp1, (1<<1) ; TimerCounter0-Überlauf-Flag löschen out tifr,temp1 ;########## ;########## ; UART wird eingestellt ldi temp,59 ; UART-Baud-rate festlegen out UBRR,temp 59 3.6. GRUNDGERÜST DES PROGRAMMS ldi temp,(1<<TXEN)|(1<<RXEN) ; uart-kontrollregister wird initialisiert out UCR, temp ;########## ;########## ; wichtige werte werden in registern abgelegt ldi temp, 3 mov synpegel,temp ldi temp, 128 mov synpegeladd, temp ldi temp, 1 mov datapegel, temp ldi temp, 64 mov datapegeladd, temp cbr flagreg, connect sbr flagreg, 128 ; niemals löschen, wichtig für hardreset ;########## Im Verlauf unserer Studienarbeit sind wir manchmal auf Probleme gestoßen, welche wir nicht lösen und teilweise nicht einmal erklären konnten. Ein solches Problem hat z.B. mit dem Interrupt INT0 zu tun. Der entsprechende Pin des Atmels ist mit einem Taster verbunden. Wird dieser Taster gedrückt, so wird der Pin auf GROUND gezogen. In den eben aufgelisteten Initialisierungen wird der Interrupt auf „Falling Edge“ gestellt. Das bedeutet, wenn der Pegel am entsprechenden Pin von High nach Low übergeht wird der Interrupt ausgelöst und in unserem Beispiel das Label „EXT_INT0“ angesprungen. Wie erwartet passierte dies nun beim Drücken des Tasters auch einwandfrei. Wurde jedoch die RESET-Taste gedrückt, so arbeitete der Atmel augenscheinlich gar nicht mehr. Er reagierte erst wieder, wenn man ihn kurzzeitig von der Spannungsversorgung trennte. Nach vielen ergebnislosen Tests stellte sich heraus, dass nach Auslösen des RESETs der Interrupt INT0 ausgelöst wurde. Eigentlich sollte dies aber unmöglich sein, da ein RESET im 60 3.6. GRUNDGERÜST DES PROGRAMMS Normalfall alle Register, also auch die Interrupt-Steuerregister löschen sollte. Deshalb haben wir das oberste Bit im „flagreg“ reserviert. Nach allen Initialisierungen wird dieses Bit auf 1 gesetzt. Wird jetzt EXT_INT0 angesprungen, so wird zuerst getestet, ob dieses Bit gesetzt ist. Ist dies nicht der Fall so springen wir direkt zurück zu den Initialisierungen. Damit war das Problem behoben und die Interruptroutine sieht nun folgendermaßen aus: ext_int0: sbrs flagreg,7 rjmp initial ; WICHTIG für hardreset ldi temp, 0x55 out udr, temp sbrc flagreg, qconnect rjmp ext_int02 sbr flagreg, connect ext_int01: ldi counter2, 0xaa ldi temp, (0<<6) out gimsk, temp ; ext_int0 wird für die Dauer ; des Handshakes ausgeschaltet reti ext_int02: cbr flagreg,connect rjmp ext_int01 61 Kapitel 4 Test der Funkstrecke Abbildung 4.1: Testaufbau für kurze Distanzen Nachdem wir die Implementierung abgeschlossen hatten wollten wir nun ganz konkret herausfinden, wie sich Funkstrecke in der Praxis bewährt. Dazu erdachten wir uns folgendes Testszenario: Jedes Board wird über ein eigenes Netzteil mit Spannung versorgt und über den Seriellen Port mit einem Rechner verbunden. Nach einem Verbindungsaufbau senden sich die Mikrocontroller gegenseitig die Werte von 0 bis 255 aufsteigend zu. Wenn mit 255 der höchste Wert erreicht wurde wird wieder bei 0 angefangen - wir senden quasi in einer Endlosschleife. Nun zählen wir die Pakete aus den 256 gesendeten, welche wir laut der Receive-Routine fehlerhaft erhalten haben 62 und geben diesen Wert nach jedem Zyklus auf die Serielle Schnittstelle aus. Diese Fehlerwerte zeichnen wir nun eine Zeitlang auf und bilden schließlich den Mittelwert. Abb. 4.2 zeigt eine solche Werteaufzeichung der Seriellen Schnittstelle. Jeder Wert entspricht den Fehlern auf 256 Paketen. Abbildung 4.2: Fehlerauswertung Diese Messung führten wir über 3 Entfernungen durch: 1m, 4m und 5m. Eine Messung auf 10m und mehr scheiterte daran, dass wir die Werte nur noch auf einem Rechner hätten aufzeichnen können. Nichts desto trotz versuchten wir dies auf dem Gang des physikalischen Instituts, leider erfolglos, da die Funkstrecke auf Grund der Umgebung schon nach wenigen Metern zusammenbrach. Auf 256 gesendete Pakete ermittelten wir folgenden Durchschnitt an falsch übertragenen Paketen auf die 3 Distanzen: Distanz Board1 Board2 1m 10,49 17,85 4m 11,93 20,65 5m 3,75 15,42 63 Alleine diese wenigen Messwerte zeigen die Problematik der Auswertung. Wie wir schon zu Beginn unserer Arbeit feststellen mussten, sind die Funkmodule hoch sensibel in Bezug auf externe Einflüsse. Die Ausrichtung zueinander, dazwischen befindliche Gegenstände und einfache Störquellen wie ein Elektromotor oder das Ein- bzw. Ausschalten eines Oszilloskops verursachten enorme Störungen. Sogar die räumliche Wahl für die Tests spielte dabei eine große Rolle, denn in privaten Räumen fielen diese Messungen wesentlich schlechter aus als im HWP-Raum der Universität, daher zeichneten wir die Messung auch dort auf. Ebenso fällt im direkten Vergleich auch der erhöhte Fehlerwert des einen Boards auf. Doch auch hier sind wir nicht in der Lage dies zu begründen und können nur nochmals betonen, dass beide Boards mit identischer Hard- und Software ausgestattet waren. Um die Sensibilität nochmals zu verdeutlichen zeichneten wir Abb. 4.3 auf. Dieser Test wurde in einem privaten Räumlichkeiten durchgeführt, Entfernung etwa 4m, dazwischen nur eine normale Zimmertür aus Holz, keine Wände. Die aufgezeichneten Werte sind diesmal nicht die gezählten Fehler, sondern die wirklich erhaltenen Daten. Da wir auch hier die Werte von 0 bis 255 übertragen haben hätte die graphische Darstellung eine aufsteigende Gerade ergeben müssen, ähnlich wie im rechten Teil des Bildes. Schließt man jedoch die Zimmertür so nahm der Paketverlust enorm zu, dies ist im linken Abschnitt zu sehen. Abbildung 4.3: Störungen der Übertragung 64 Auf Grund der angesprochenen Tatsachen entschlossen wir uns keine weiteren Testreihen aufzuzeichnen, da man daraus keine allgemein gültigen Folgerungen hätte ziehen konnte. Die äußeren Einflüsse auf die Ergebnis sind zu groß, sodass ein genaues Verhalten nicht vorhergesagt werden kann. Um dennoch die gesamte Funktionsweise des Protokolls und der Datenübertragung zu Testen ersetzen wir die Funkmodule durch eine Kabelverbindung und zählten erneut die Fehler auf je 256 Paketen. Abb. 4.4 zeigt ein zu unserer Zufriedenheit deutliches Ergebnis, da nicht ein Paket fehlerhaft übertragen wurde. Abbildung 4.4: Fehleranteil per Kabelübertragung 65 Kapitel 5 Chronologie In diesem Kapitel möchten wir zunächst den Verlauf unserer Arbeit beschreiben und die einzelnen Arbeitsschritte darstellen. Dabei wollen wir auch gezielt vorgekommene Probleme ansprechen und dazu Stellung nehmen. Im zweiten Abschnitt nehmen wir dann nochmals Bezug auf das Erreichen unserer Zielsetzung. 5.1 Verlauf Da wir auf dem Gebiet des Mikrocontrollers zu Beginn nur auf die wenigen Erfahrungen aus dem Hardwarepraktikum zurück greifen konnten, begann unsere Arbeit zunächst mit einer intensiven Einarbeitungsphase. Wir eigneten uns die wesentlichen Eigenschaften des Atmel AT90S2313 aus dessen Datenblatt an und informierten uns über die restlichen Bauteile. Danach begannen wir mit dem Zusammenstecken erster Schaltungen auf einem Steckboard. Um nun die Basis für unsere weitere Entwicklung abzuschließen fehlte uns nun noch die passende Software, welche wir durch kurzes Suchen im Internet und anschließendem Testen schnell aussortieren konnten. Für die weiteren Arbeitsschritte verwendeten wir die in Kapitel 2.5 auf Seite 16 angesprochenen Programme. Von nun an versuchten wir uns mit ersten einfachen Programmen zur Ein- und Ausgabe auf dem Mikrocontroller, welches wir über LEDs kontrollierten. Recht bald mussten wir einsehen, dass die zusammengesteckten Boards sehr Fehleranfällig waren in Bezug auf lockere Kabelbrücken und die Suche nach diesen Fehlern sehr zeitaufwändig war. Daher entschieden wir uns nach etwa 66 5.1. VERLAUF einer Woche der Experimentierphase feste Boards auf Lochrasterplatinen zu löten. Diese Boards stellten von nun an die Grundlage für alle weiteren Aktivitäten und wurden später nur nochmals leicht modifiziert. Abbildung 5.1 zeigt bereits die modifizierte und damit finale Version. Abbildung 5.1: Atmel-Board 1 Abbildung 5.2: Atmel-Board 2 67 5.1. VERLAUF Nun, da die Basis gelegt worden war, konnten wir mit den konkreten Planungen beginnen und machten erste theoretische Entwürfe zur Wait-Methode und deren Realisierung. Nach ihrer Implementierung und kurzer Testphase begaben wir uns dann an die Methoden Transmit und Receive, zunächst wieder in der Theorie und im Anschluss ganz konkret in der Implementierung. Dies testen wir zunächst über eine Kabelverbindung in nur eine Richtung, d.h. ein Board sendete fortlaufend Pakete, während das zweite die Werte auf die Serielle Schnittstelle ausgab. Schon hierbei wurde uns bewusst, dass wir beim Messen der Pegel und dem Abwarten auf den richtigen Mess-Zeitpunkt strenge Zeitkriterien erfüllen mussten, welchen wir später noch Tribut zollen sollten. Um die Gefahr einer Asynchronität und damit einem Paketverlust vorzubeugen, entschieden wir uns zunächst dafür, dass die Receive-Methode sich mit jedem erhaltenen Bit des Manchester-Codes selbst an der wechselnden High-Low bzw. Low-High Flanke synchronisieren sollte. Dies implementierten wir zunächst wie angesprochen und der Sende- und Empfangsbetrieb funktionierte bis dato per Kabel einwandfrei. Nun galt es, die bisherigen Ergebnisse auf eine Funkstrecke zu übertragen. Parallel zu den oben angegebenen Arbeitsschritten verlief die Suche nach geeigneten Funkmodulen. Um die Eignung von Modulen für unsere Studienarbeit zu beurteilen unterzogen wir sie alle dem selben Testaufbau: Sender und Empfänger wurden mit der notwendigen Spannung versorgt, das zu übertragende Signal per Frequenzgenerator angelegt und das Empfangsignal auf einem Oszilloskop untersucht. Damit hatten wir die Möglichkeit die Übertragungsrate der Module zu variieren und Pegelveränderung sowie die Frequenz-Grenzen der Module festzustellen. Zu Beginn testeten wir die Verwendung der Funkmodule eines Mini-Racers, welche man hin und wieder aus den Werbegeschenken verschiedener Großmarktketten kennt, doch mussten wir feststellen, dass die übertragenen Pegel sehr unsauber und damit für den Betrieb am Mikrocontroller ungeeignet waren. Ebenso erging es uns mit Modulen auf 433MHz Basis von Conrad. Schließlich entschieden wir uns für die Module der Firma IKHF, da sie auf der von uns angestrebten Übertragungsrate die saubersten Pegel lieferten. Im weiteren Verlaufe stießen wir jedoch auch mit diesen Modulen auf einige Probleme in Bezug auf die Reichweite und Zuverlässigkeit und so testeten wir nochmals andere Module auf Bluetooth-Basis. Doch diese Module hatten die Eigenschaft nur auf festgelegten Baud-Raten zu senden und verpackten unsere Datenpakte dazu noch in das bekannte 8-N-1-Format, an welches eine Anpassung unserer bisherigen Arbeit den Zeitrahmen deutlich überschritten hätte. Daher blieben wir bei den Modulen der Firma IHKF. 68 5.1. VERLAUF Nachdem wir nun die Modul-Wahl entschieden hatten schlossen wir sie zunächst über Kabelverbindungen an. Doch auch hier galt das gleiche wie für die Steckboards ganz zu Beginn, die Fehlersuche bei losen Kabeln oder Wackelkontakten war zu aufwändig und nervenaufreibend. Daher musste eine feste Steckmöglichkeit geschaffen werden und wir modifizierten die anfänglichen Boards zum endgültigen Schaltplan, wie man ihn in Abschnitt 2.3.2 auf Seite 12 sehen kann. Die Schwierigkeiten hierbei bzgl. der Spannungsversorgung wurden in Abschnitt 2.6 auf Seite 13 dargestellt. Das nächste Ziel war nun die bestehende einseitige Kabelübertragungsstrecke auf eine Funkstrecke auszubauen. An dieser Stelle gab es gleich mehrere Probleme, erwartete aber auch unerwartete, welche uns zu Anfang die Aussicht auf Erfolg in weite Ferne rückten. Eines der erwarteten Probleme war es, den Paketanfang richtig zu erkennen. Unsere Funkmodule hatten die Eigenschaft generell zwei verschiedene Rauschen zu empfangen. Zum einen war es ein GrundRauschen, wenn keine Daten gesendet werden und zum anderen ein Rauschen, welches sich dann einstellt, wenn der Sender einen zu langen Low- oder High-Pegel anlegt. Dies würde zum Abbruch des Signals führen, da die Module eine Mindestbaudrate vorschrieben. Nun galt es aus diesen beiden Rauschen unsere Pakete richtig herauszufiltern. Dazu gab es zwei Möglichkeiten: einen geeigneten Erkennungsmechanismus wie eine Präambel vornweg zu übertragen oder ein ständiges Signal zu senden, damit ein abbrechen der Sendestrecke zw. den Paketen nicht möglich wäre. Letzteres wäre über eine Interrupt-Routine realisierbar gewesen, durch welche der Sender auch im Empfangs-Status eines neuen Paketes immer wieder Daten gesendet hätte. Doch alleine der Programmieraufwand stand in keinen Verhältnis zur möglichen Erfolgsaussicht und die Tatsache, dass wir später in beide Richtungen auf der gleichen Frequenz senden wollten, würde uns dazu zwingen immer eine Sendestrecke komplett abzustellen. Also konnten keine dauerhaften Signale übertragen werden, daher blieb nur der Weg über die Präambel. Doch auch deren Gestaltung war nicht trivial, denn wir konnten nach dem Einschalten der Sender nicht immer garantieren, wie viele unserer Präambel-Pegel übertragen wurden. Daher ist der von uns nun verwendete Wert aus praktischen Tests ermittelt worden, da wir somit eine sichere Erkennung der Präambel garantieren können. Eines der unerwarteten Probleme dagegen waren auftretende Peaks innerhalb eines Low- oder High-Pegels unserer Daten. Hin und wieder traten diese auf und führten dazu, dass unsere angestrebte Synchronisation an jedem Bit, welche auf die steigende bzw. fallende Flanke innerhalb 69 5.2. BEURTEILUNG eines Bits wartete, aus dem Takt geriet und das Paket verworfen wurde. Dies stellte sich als nicht filterbares Problem heraus und wir mussten unsere Synchronisation neu überdenken. Daher entschieden wir uns, die Synchronisation an der Flanke innerhalb eines Bits aufzugeben und gingen dazu über, feste Zeitabstände zu warten, um jeweils die Mitte der beiden zu einem Bit gehörenden Pegel zu treffen und dort zu messen. Dies funktionierte auch soweit, doch wurden nun die Paketfehler größer, welche dafür sprachen, dass wir mit unserer Wait-Routine nicht präzise genug warteten (somit also nicht immer die Mitte der Pegel trafen und aus dem Takt kamen). Also musste auch die Wait-Routine nochmals neu überdacht und takt-präziser implementiert werden. Dies ist mit der vorliegenden Wait-Routine umgesetzt. Nach der funktionierenden Funkübertragnung entwarfen wir nun das Protokoll und implementierten dieses auf beiden Boards. In der ersten Version des Protokolls verzichteten wir zunächst auf das Verhalten des passiven Boards im Fehler Fall, d.h. falls der Empfänger einen Fehler beim Empfangen eines Datenpaketes meldete sollte er solange warten, wie die Sendedauer des Paketes andauert und erst dann wieder senden. Doch dies zog logischerweise Folgefehler nach sich und so musste die Receive-Methode nochmals angepaßt werden. Nachdem wir nun die Implementierung abgeschlossen hatten begaben wir uns an die Aufzeichnung verschiedener Testreihen, deren Ergebnisse wir in Kapitel 4 auf Seite 62 zeigten. Anschließend erfolgt die Erstellung dieser Ausarbeitung. 5.2 Beurteilung Zum Abschluß unserer Studienarbeit möchten wir nochmals kurz die Chance nutzen um über unsere Arbeit und das erzielte Ergebnis zu resümieren. Generell sehen wir unsere Zielsetzung durch die von uns aufgebaute Funkstrecke erreicht, zumindest was die theoretischen Grundlagen angeht. Jedoch müssen wir auch einen recht kritischen Standpunkt einnehmen, wenn es um die Beurteilung der praktischen Relevanz dieser Funkstrecke geht. Dies liegt weniger an der von uns aufgebauten Theorie als vielmehr an den verwendeten Funkmodulen. Diese erwiesen sich im Betrieb als sehr empfindlich und hoch sensibel im Bezug auf jegliche Art von Störquellen. Sicherlich sind wir uns bewusst, dass wir diese Empfindlichkeiten durch verschiedene Maßnahmen, wie z.B. die vollkommene Abschirmung oder eine weitere räumliche Trennung der Module, 70 5.2. BEURTEILUNG hätten minimieren können. Auch durch die Implementation von Mehrfachmessungen je Pegel und anschließender Mittelwertbildung (ähnlich dem UART-Standard) oder der Verwendung eines Parity-Bits wäre es möglich gewesen Übertragungsfehler zu minimieren bzw. zu erkennen. Da diese Art von Fehlern jedoch sehr selten auftraten implementierten wir keinen solchen Algorithmus. Ebenso sind die Aspekte der Hochfrequenztechnik ein weiteres sehr großes Gebiet, dessen Erschließung den Rahmen unserer Arbeit überstiegen hätte. In diesem Bereich sehen wir auch die größten Möglichkeiten das Ergebnis unserer Arbeit bzgl. der Reichweite und Störempfindlichkeit zu verbessern. Ein Einsatz dieser Funkstrecke im geplanten Projekt sehen wir leider als sehr unwahrscheinlich an, da das Modellauto mit einem Elektromotor betrieben werden soll und dies wie bereits erwähnt zu enormen Störungen führt. Bilanzieren wir nun das Gesamtergebnis, so sind wir sicherlich mit dem erreichten Ergebnis aus der Sichtweise der Informatik zufrieden. Lediglich die Anfälligkeit der Funkstrecke trübt ein wenig das Bild, doch wäre dies, wie schon erwähnt, auf Grund unserer Unerfahrenheit auf dem Gebiet der Hochfrequenztechnik nicht trivial zu lösen gewesen. Wir bedanken uns für die kompetente und freundschaftliche Betreuung bei Dr. Merten Joost und den Mitarbeitern des physikalischen Institutes der Universität Koblenz. 71 Kapitel 6 Anhang 72 6.1. ANHANG A: SCHALTPLAN 6.1 Anhang A: Schaltplan Abbildung 6.1: Schaltplan 73 6.2. ANHANG B: LITERATURVERZEICHNIS UND CD-ROM ANLAGE 6.2 Anhang B: Literaturverzeichnis und CD-Rom Anlage Hier nun nochmals alle Angaben zu den verwendeten Programmen und Datenblättern. AVR Studio 4.10 www.atmel.com SP12 und TwinAVR 1.1.0.1 (früher WinAVR) www.rowalt.de/mc/avr/progd.htm AVRTerm 1.2.0.4 www.rowalt.de/mc/avr/toolsd.htm Atmel AT90S2313 Datasheet www.atmel.com Atmel Instruction Set www.atmel.com AVR Seite von Walter Rowalt www.rowalt.de Die Quelltexte der beiden Atmelboards befinden sich auf der beiliegenden CD-Rom und können dort bei Bedarf nochmals eingesehen werden. Ebenso finden sich dort Fotos zu den beiden Boards, deren Schaltplan, das Atmel-Datasheet sowie diese Ausarbeitung im PDF-Format. 6.3 Anhang C: Erklärung Hiermit erklären wir, Ralf Töppner (Mat.Nr.201210387) und Daniel Schüller (Mat.Nr. 201210267), die vorliegende Studienarbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt zu haben. Koblenz, den 25. April 2005 Ralf Töppner Daniel Schüller ([email protected]) ([email protected]) 74