„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

Documentos relacionados