Intelligente Datenübertragung per Funk

  Hardware   Software-Konzept   Senden   Empfangen   Bibliographie
Wie überträgt man Datenpakete über drahtlose Kanäle? Die erste Idee: man setze den Sender an TxD des UART, den Empfänger an RxD, nehme eine möglichst langsame Baudrate, die grade für den Einsatzzweck ausreicht, verwende ein Paritätsbit, und fertig ist die Datenübertragung. Spätestens nach dem ersten Test zeigt sich jedoch, dass das nicht ganz so einfach ist wie gedacht. Diese Seite zeigt, wie man eine kleine Fernbedienung per Funk aufbauen kann, die dann auch tatsächlich funktioniert.

Die Sende- und Empfangshardware

Die erste Frage ist: woher ein paar Funksender/empfänger nehmen? Die meisten Sender/Empfänger-Kombinationen sind gradezu lächerlich teuer - nichts zum verbasteln. Selbstbaulösungen sind mangels geeigneter Ausrüstung nicht machbar, und ausserdem auch noch illegal. Die billigste Lösung, die aufzutreiben war, bestand aus den Bausätzen für 1 Kanal-Fernsteuerungen von Conrad.

Sende- und Empfangsmodul

Für 40 DM waren die Sender, für 80 DM die Empfänger zu bekommen. Die HF-Teile waren jeweils auf einer fertig aufgebauten Modulplatine vorhanden, das restliche Bausatzgemüse konnte man der Bastelkiste zuführen.

Sender/Empfänger waren allerdings wirklich billig - in jeder Hinsicht. Die 100m Reichweite sind allenfalls Wunschdenken, die angegebenen 20m unter gestörten - also realen - Bedingungen schon eher nachvollziehbar. Der Enable-Pin des Senders ist in Wirklichkeit die Spannungsversorgung. Dies hat zwei negative Nebeneffekte: nachdem man Enable gesetzt hat, muss der Empfänger sich noch ca. 15ms aufladen, einschwingen oder was auch immer, bevor er sendebereit ist. Des weiteren sendet das Modul nach Abschaltung des Enable einige Millisekunden willkürliche Signale, bevor es völlig entladen ist.

Das Empfangsmodul begnügt sich mit 5V. Auch das Sendemodul lässt sich mit 5V betreiben, der Maximalwert von 14V ist aber angebrachter, um die Reichweite zu erhöhen. Am einfachsten lässt sich der Sender über Optokoppler wie die 4N25 schalten, da diese grade genug Leistung bieten, um den Sender direkt anschließen zu können, im abgeschalteten Zustand praktisch keinen Strom verbrauchen, und zudem relativ klein sind.
Zum Einbau des Senders eignet sich eine größere Fernbedienung. Im Pollin-Katalog fand sich dazu eine Video-TV Fernbedienung von Telefunken für nur 3 DM.

Fernbedienungsgehäuse

Mit Batteriefach, ordentlich Platz im Innenraum und 21 Tasten, die über ein Folienkabel angeschlossen sind, ist dieser Typ für den Einbau einer eigenen Elektronik ausgezeichnet geeignet.

eingebaute eigene Elektronik



Der Empfänger zeigt sich ziemlich zickig bei Störungen auf der Eingangsleitung. Nach dem Einbau in ein Gehäuse aus miteinander verlöteten Leiterplatten mit Masseanschluss und der Filterung der Spannungsversorgung mittels Drosseln und Kondensatoren legt sich dieses Problem glücklicherweise.
zurück zum Anfang

Das Software-Konzept

Mit einem regelbaren Funktionsgenerator lässt sich die nutzbare Bandbreite der Sender/Empfänger-Kombination leicht ausprobieren. Bei den Conrad-Modulen können noch Pulse mit 0.2ms Länge zuverlässig unterschieden werden. Ist ein gesendeter Puls länger als 10ms, zeigt sich der Empfänger irritiert und gibt falsche Werte aus. Somit lassen sich ohne Probleme 50 Bit mit 0.2ms Bitzeit am Stück übermitteln, ohne sich Sorgen um deren Inhalt machen zu müssen. Längere Bit-Streams müssten entweder durch Pausen unterteilt werden, oder durch Bitstuffing künstlich mit Pausen angereichert werden.

Für die Fernbedienung, die die Grundlage für diese Beschreibung ist, genügen 16 Bit. Funkübertragungen, erst recht auf einem lizenzfreien Band, sind oft starken Störungen unterworfen.
Daher sind die letzten 8 Bit für eine wirklich zuverlässige Fehlererkennung reserviert.

Warum sich Gedanken machen, wenn eine zuverlässige Lösung längst existiert? Daher wird zur Fehlererkennung der CRC8-Algorithmus in der Implementierung von John Wren, gefunden auf www.8051.com, eingesetzt. Grob vereinfacht funktioniert dieser Algorithmus dadurch, dass die Datenbits durch einen Generatorwert geteilt werden. Der dabei übrigbleibende Rest ist nun die CRC-Prüfsumme. Weil 0 durch einen beliebigen Wert wieder 0 ergibt, und dabei Daten und Prüfsumme identisch wären, ist es angeraten, vor der Prüfsummenberechnung einen willkürlichen Startwert einzustellen.
zurück zum Anfang

Senden

Da nun Hardware und Grobkonzept festliegen, steht nun auch der Ablauf des Sendens weitestgehend fest. Dieser sieht so aus:
 INCLUDE crc8.inc
 INCLUDE time24.inc

HFOUT	EQU	P3.1
HFEN	EQU	P3.0
Zunächst werden einige Ersatzvariablen belegt und die CRC8-Libary eingebunden, desweiteren eine Bibliothek für Warteschleifen.
HFOUT: Datenpin, Active Low
HFEN: Enable-Pin, Active Low
send_data:
    push 0
    push b
    push Acc
Diese Funktion wird zum Senden aufgerufen. Dabei befinden sich die Sendedaten in Acc.
    clr HFEN
    setb HFOUT
    push Acc
    mov a,#15
    call F_wait_m
    pop Acc
Hier wird der Sender 15 ms 'vorgeheizt'. Dafür wird eine Funktion in der Warteschleifen - Bibliothek aufgerufen.
    mov b,#10011101b
    call crc8_add_calc
CRC-Zähler mit Startwert initialisieren und Prüfsumme berechnen
Radio_out:
    mov R0,#8
Hier beginnt nun die eigentliche Senderoutine. Gesendet werden alle 16 Bytes an Stück, eingeleitet von einem Startbit. Die Inputbytes sind in Acc und B.
    clr HFOUT
    call Radio_L3
    call Radio_L3
    nop
Startbit ausgeben. Bis zur Ausgabe des 1. Bits sind es 398 Maschinenzyklen.
Radio_L2a:
    rrc a
    mov HFOUT,c
    call Radio_L3
    call Radio_L3
    djnz R0,Radio_L2a
Erstes Byte ausgeben. Der Durchlauf durch diese Schleife dauert genau 401 Maschinenzyklen. Dabei wird zunächst mit einem Rotationsbefehl das zu sendende Byte Bit für Bit in das Carry-Flag geschoben, und von dort in den Ausgang kopiert. Daraufhin wird 0.2ms gewartet.
    mov a, b
    mov R0,#8
Byte wechseln und R0 wieder initialisieren.
Radio_L2b:
    rrc a
    mov HFOUT,c
    call Radio_L3
    call Radio_L3
    djnz R0,Radio_L2b
2. Byte ausgeben
    setb HFOUT
    setb HFEN

    pop Acc
    pop b
    pop 0
    ret
Das war's: Sender abstellen und gesicherte Register wiederherstellen.
Radio_L3:
    push psw
    push 0
    mov  0,#92
Radio_L3_labelA:
    djnz 0,Radio_L3_labelA
    pop 0
    pop psw
    ret
Intern verwendete Warteschleife, die 198 Maschinenzyklen verbrät.
zurück zum Anfang

Empfangen

Das Empfangen ist nicht ganz so einfach wie das Senden. Im Prinzip könnte man auch beim Empfang eine Warteschleife durchlaufen lassen, und für jedes Bit einen 0/1-Wert in der Mitte von dessen Bitzeit abgreifen. Dies funktioniert bei einer fehlerarmen Übertragung ganz gut, und der Hardware-UART scheint auch genau so zu arbeiten.

Allerdings sieht eine Funk-Übertragung nicht so schön sauber aus wie eine kabelgebundene. In der Illustration, die oben die gesendeten und unten die empfangenen Signale abbildet, kann man einige Störungen im Empfänger erkennen.


Zum einen hat die Synchronisation mit dem Startbit nicht ganz geklappt, so sind die empfangenen Signale leicht auf der Zeitachse verschoben. Zum anderen sind die gesendeten Bits von Störungen unterbrochen. Bei nur einer Abfrage des Wertes pro Bit wäre es purer Zufall, ob das gesendete Signal oder eine Störung aufgezeichnet würde.
Die Lösung des Problems besteht darin, das Eingangssignal möglichst kontinuierlich abzufragen und die erhaltenen 0/1-Werte aufzusummieren. Die Summe einer Bitzeit kann dann mit einem Schwellwert verglichen werden. Ist der aufsummierte Wert größer als eine bestimmte Konstante, so ist das Bit 1, sonst 0.

Das erste zu empfangende Bit ist das Startbit, welches immer 1 beziehungsweise ein gesendeter Puls sein muß, weil sich der Empfänger daran synchronisiert. Er weiß nach dem Erhalt des Startbits, dass noch 16 weitere Bits folgen. Auf den Empfang des Startbits wird in diesem Programmbeispiel in einer Schleife gewartet. Da diese Schleife aber nicht nur verlassen wird, wenn ein Startbit empfangen wird, sondern auch, wenn nur ein gesendeter Störungsimpuls eintrifft, würde der Empfänger daraufhin 16 Bitzeiten sinnlos mit dem Aufzeichnen von falschen Werten vertun. Damit dies nicht geschieht, wird auch das Startbit über dessen Bitzeit aufsummiert. Ist diese Summe kleiner als eine Konstante, so war es kein Startbit, sondern nur eine empfangene Störung; die Suche nach einem gültigen Startbit kann weitergehen.

zurück zum Anfang
 INCLUDE crc8.inc

HFIN        EQU    P3.2

main:

Radio_in:
    clr a
    mov R0,#8
    mov R1,#98
    mov R2,a
    mov R3,a
Den Empfang vorbereiten. R0 enthält den Zähler für die äußere Schleife, 8 Durchläufe für 8 Bits. R1 enthält den Zähler für die innere Schleife. Dieser ist so angelegt, dass ein Durchlauf zusammen mit den Befehlen davor und danach genau eine Bitzeit lang dauert. R2 und R3 nehmen das Datenbyte und die Prüfsumme auf.
Radio_L0:
    jnb HFIN,Radio_L0
Hier wird auf das Startbit gewartet. Die Schleife wird erst dann verlassen, wenn HFIN 1 ist.
Radio_L0a_innerLoop:
    mov c,HFIN
    addc a,#0
    djnz R1,Radio_L0a_innerLoop
An dieser Stelle wird das Startbit empfangen. Dazu wird in einer Schleife zunächst der empfangene 0/1-Wert in das C-Flag geschrieben, und dieses dann mittels ADDC zu A addiert.
    clr c
    subb a,#60
    jc main_error
Bei einem optimalen Empfang müsste HFIN die ganze Bitzeit lang 1 gewesen sein. Bei 98 Schleifendurchläufen hätte also A den Wert 98. Weil die Übertragung aber alles andere als perfekt ist, wird A mit einer kleineren Konstante verglichen. War das Startbit nicht 1, so wurde nur eine Störung empfangen, es kann zu einer Fehlerbehandlungsroutine verzweigt werden.
Radio_L1a:
    mov R1,#98
    clr a
Da augenscheinlich ein korrektes Startbit eingetroffen ist, wird nun das 1. Byte empfangen.
Radio_L1a_innerLoop:
    mov c,HFIN
    addc a,#0
    djnz R1,Radio_L1a_innerLoop
Dazu wird wieder das Signal für jedes Bit aufsummiert
    clr c
    subb a,#60
und mit einer Konstante verglichen. Der Vergleich sieht so aus, dass eine Konstante vom empfangenen Wert abgezogen wird. War der empfangene Wert kleiner dieser Konstante, so trat bei der Subtraktion ein Überlauf auf, und das C-Flag ist gesetzt, anderenfalls gelöscht.
    mov a,R2
    rrc a
    mov R2,a

    djnz 0,Radio_L1a
Das C-Flag wird nun mittels 'Rotate right trough the C-Flag' in ein Byte geschoben. Nach 8 dieser Rotationen ist das Byte komplett und jedes Bit an seiner richtigen Stelle.
    mov R0,#8

Radio_L1b:
    mov R1,#98
    clr a

Radio_L1b_innerLoop:
    mov c,HFIN
    addc a,#0
    djnz R1,Radio_L1b_innerLoop

    clr c
    subb a,#60

    mov a,R3
    rrc a
    mov R3,a

    djnz 0,Radio_L1b
Das selbe Spielchen wiederholt sich nun für das zweite Byte, das die Prüfsumme enthält.
    mov a,R2
    mov b,#10011101b
    call crc8_add_calc
Nachdem nun beide Bytes empfangen wurden, wird aus dem Datenbyte wieder die Prüfsumme errechnet.
    mov a,R3
    xrl a,b
    jnz main_error

;   [Daten verarbeiten]

    jmp main

main_error:
;   [Fehlerbehandlung]
Ist die errechnete Prüfsumme identisch mit der empfangenen, so war diese Übertragung erfolgreich, und das Datenbyte kann verarbeitet werden. Anderenfalls wird zu einer Fehlerbehandlungsroutine verzweigt. Beispielsweise könnte dort ein Fehler-Pieps ausgegeben werden.
zurück zum Anfang
Dieser Code wurde mit dem Ziel entwickelt, eine Funk-Übertragung zuverlässig zu senden und zu empfangen. Prinzipiell eignet er sich jedoch für alle drahtlosen Übertragungen, da überall das gleiche Maß an Unzuverlässigkeit zu erwarten ist. Beispielsweise entstehen bei einer Infrarot-Übertragungsstrecke durch Sonnenlicht, das von bewegenden Blättern reflektiert, kurze Störimpulse.
zurück zum Anfang

Bibliographie

http://www.epanorama.net/radio.html Radio Electronics Pages, allgemeines zu Funk
http://www.epanorama.net/opto.html Optoelectronics and optics page
http://www.isocomoptocouplers.com/isocom/4n25.htm 4N25, Optically Coupled Isolators
http://www.8052.com/codelib.phtml 8051 Code Library, CRC-Code



Seite zurück Startseite Startseite
Erik Buchmann
EMail an: Owner@ErikBuchmann.de