Wenn man sich etwas intensiver mit Mikrocontrollern beschäftigt, kommt früher
oder später der Wunsch auf, daß die einzelnen aufgebauten Module Daten
untereinander austauschen können sollen. Auch der Anschluß an eine
serielle Schnittstelle am PC ist oftmals recht nützlich. Natürlich gibt
es bereits Lösungen für diese Problematik, z.B. I2C oder SPI. Andererseits
sind bei meinen Projekten (ich mag eben diese kleinen "8-Beiner-Controller")
Portpins Mangelware, und diese Schnittstellen erfordern meist mehrere Ports. Also fiel
meine Wahl auf einen "Eindrahtbus", bei dem für die Kommunikation außer
einer gemeinsamen Masseleitung nur eine Leitung benötigt wird. Auch dafür
existieren bereits Lösungen, die jedoch ein recht kompliziertes Protokoll zur
Adressierung der einzelnen "Stationen" verwenden, und oft einen "Busmaster"
benötigen. Da ich für meine Zwecke auch keine sonderlich hohen Datenraten
benötige, kam ich auf den Gedanken, einfach eine "bidirektionale, serielle
Schnittstelle" mit 9600Bd zu verwenden.
|
Das "Hardware-Layer"
|
Natürlich könnte ich einfach die Portpins zweier (oder mehrerer)
Mikrocontroller direkt miteinander verbinden. In diesem Fall könnte
jedoch der Zustand auftreten, daß ein Modul die gemeinsame Leitung
(den Bus) auf "High" legt, während ein anderes Modul die Leitung
auf "Low" legt -> Unschön und bestimmt nicht sehr verträglich
für die Ausgangstreiber der Portpins. Gut, dieses Problem lässt sich
durch die Verwendung eines "Open Collector"-Busses vermeiden, bei dem
der Bus mit einem Widerstand ("Pullup") auf die Betriebsspannung gelegt wird,
und die Ports entweder den Bus "auf Low ziehen", oder als Eingang (hochohmig)
geschaltet sind. Somit legt kein Controller eine Spannung auf den Bus, und es können
nie zwei Ports "gegeneinander arbeiten". Dabei ergibt sich jedoch die Frage,
welches Modul denn den "Pullup"-Widerstand mit Betriebsspannung versorgt.
Ein weiteres Problem ergibt sich aus dem Wunsch, daß nicht alle am Bus
angeschlossenen Module auch mit Betriebsspannung versorgt sein müssen, was
die Flexibilität dieses System deutlich erhöht. In solch einem Fall
sollte gewährleistet sein, daß der Portpin des abgeschalteten Moduls
nicht von Bus her mit Spannung beaufschlagt wird, was für die Porttreiber
bzw. die Schutzdioden des abgeschalteten Mikrocontrollers recht schädlich
sein könnte. Nach etlichen Überlegungen und Experimenten ergab sich
die nebenstehende – überrachend einfache – Schaltung, die
gleichzeitig auch noch das Problem des "Pullup"-Widerstandes
eliminiert: Ist mindestens ein Modul mit Spannung versorgt, versorgt dieses
Modul auch den Bus.
|
Das "Low-Level-Protokoll"
Da auf dem Bus sämtliche Stationen/Module gleichberechtigt sind (ein "Busmaster"
ist nicht erforderlich), und demzufolge auch gleichberechtigt Daten senden können,
muss möglichst verhindert werden, daß zwei angeschlossene Module
gleichzeitig senden, und auf dem Bus "Datenmüll" entsteht. Dazu
habe ich mir mal angesehen, wie dieses Problem z.B. beim (schon etwas älteren)
10base2-Netzwerk
(welches im Prinzip ja auch ein "Eindrahtbus" ist) gelöst wird: Dort
wird ein Verfahren names
CSMA/CD verwendet,
welches ich (etwas modifiziert) für meinen Zweck nachgebildet habe: Der Zustand
der Busleitung wird vom Mikrocontroller kontinuierlich geprüft, und ein Zähler
(bei mir alle 104µs) inkrementiert, wenn keine Kommunikation auf dem Bus entdeckt
wird. Wird eine Aktivität auf dem Bus erkannt, wird der Zähler auf Null
gesetzt. Liegt eine Anforderung der Firmware im Controller vor, Daten zu senden, wird
diese Anforderung so lange ignoriert, bis dieser Zähler einen Wert von mindestens
16 erreicht hat. Erst dann zieht der Controller den Bus auf "Low", und legt
damit sein Startbit auf den Bus. 104µs (→ 9600Bd) später folgt das
erste Datenbit, genau wie bei der Übertragung auf einer seriellen Schnittstelle.
Wird ein "1"-Bit gesendet, schaltet der Controller seinen Portpin auf
"Input", was bewirkt, daß der Buspegel (durch den Pullup-Widerstand)
auf "High" geht. Am Ende eines solchen Bits wird gepüft, ob der Bus
auch wirklich auf "High" liegt. Ist dieses nicht der Fall, ist davon
auszugehen, daß noch ein anderes Modul momentan auf dem Bus sendet, und eine
Kollision aufgetreten ist. In diesem Fall zieht der Controller die Busleitung für
12 Bitlängen (a 104µs) auf "Low", damit auch das andere sendende
Modul (spätestens bei Stopbit) eine Kollision bemerkt. Beide sendenden Station
wiederholen ihre Sendung jeweils nachdem ihr "Bus frei"-Zähler wieder
16 erreicht hat. Die Wahrscheinlichkeit, daß beim nächten Versuch wieder
beide Stationen gleichzeitig senden, ist aufgrund der sehr kurzen Zeitspanne zwischen
"Bus prüfen" und "Startbit senden" recht gering, sodaß
ich mir die "zufällige Verzögerung", die beim CSMA/CD-Verfahren
vorgesehen ist, erspart habe. Eine während einer Datenkollision im
"Empfangszustand" befindliche Station erkennt das fehlerhafte Stopbit bei
der Datenübertragung, somit auch die Kollision, und verwirft das empfangene
Datenbyte. Damit ein Modul, welches mehrere zusamenhängende Bytes (ein
"Datagram") senden möchte, nicht durch ein anderes Modul unterbrochen
werden kann, beträgt die "Wartezeit", bis ein weiteres Datenbyte gesendet
werden darf, nach einem bereits gesendetem Datenbyte nur nur 2-3 Zählerschritte.
|
|
Implementierung in der Firmware
Sowohl die Ansteuerung der Busleitung, als auch die Behandlung des "Low-Level-Protokolls"
erfolgt vollständig innerhalb des Interrupt-Handlings. Die Interaktion mit dem eigentlichen
"Applikationsprogramms" in der Firmware des Mikrocontrollers erfolgt mit Hilfe von
zwei Registern für die empfangenen und zu sendenden Datenbytes, und ein paar Flagbits, also
ähnlich einem UART. Da die exakte Implementierung sehr eng mit der Anwendungs-Firmware
verwoben ist (weitere Interrupts, regelmäßige Prüfung der Flagbits, ggf.
notwendige Datenpufferung, ...), existiert kein "universeller" Code für diese
Funktionalität → Das Bushandling ist u.A. abhängig vom verwendeten Controller
und der benutzten Taktfrequenz, und daher bei fast jeder Anwendung etwas anders implementiert.
Aus diesem Grund existieren auch keine Bibliotheksfunktionen für diese Funktionalität.
Der jeweils angepasste Code zur Ansteuerung dieses Busses ist demzufolge Bestandteil des Codes
einer bestimmten Anwendung.
|
Höhere Protokollebenen
Je nach Anwendungsfall und Anzahl der am Bus angeschlossenen Stationen ist eine Adressierung
einzelner Stationen notwendig, aber manchmal reicht auch eine geschickte Codierung der
versendeten Datenbytes, um nach dem Empfang zu unterscheiden, ob ein Datenbyte für ein bestimmtes
Modul relevant ist, oder ob es einfach ignoriert werden kann. Diese (und ggf. noch höhere)
Protokollebenen sind demzufolge Bestandteil der Applikation. Das Einzige, was das
"Low-Level-Protokoll" dafür anbietet, ist die Möglichkeit, mehrere Datenbytes
mit einer so kurzen "Bus-Free"-Pause zwischen den einzelnen Bytes zu versenden,
daß kein anderes Modul während der Sendung zusammengehöriger Daten (Datagram)
den Bus belegen kann. Die Voraussetzung dafür ist nur, daß die Applikation schnell
genug (ggf. aus einem Puffer) Daten an das "Low-Level-Protokoll" übergeben kann.
|
Grenzen des Systems
Durch die relativ geringe Übertragungsgeschwindigkeit von 9600Bd und einem relativ
großen Protokoll-Overhead ist das vorgestellte Verfahren nicht zur Übertragung
größerer Datenmengen geeignet, aber zum gelegentlichen Auslesen von Sensoren
oder der Ansteuerung eines kleinen Text-Displays reicht es aus. Da die verwendete
Busleitung fast direkt an den Controllern angeschlossen ist, sollte die Länge
der Busleitung einige, wenige Meter nicht überschreiten. Bei Buslängen
größer als 1m sind zusätzliche Schutzmaßnahmen (Zenerdioden,
Varistoren?) an den Modulanschlüssen zur Begrenzung von ggf. in der Busleitung
induzierten Spannungen sicher empfehlenswert.
|
Serielles Interface
Diese Seite beschreibt meine Lösung für eine einfache Kommunikation zwischen Mikrocontrollern, und erhebt keinen Anspruch auf Allgemeingültigkeit. Wer das hier vorgestellte Verfahren verwenden möchte, sollte über entsprechende Programmier-Kenntnisse verfügen. Alles, was ich dazu anbieten kann, befindet sich auf dieser Seite, d.h. Nachfragen nach Code-Bibliotheken für irgendwelche Microcontroller sind zwecklos → Ich arbeite ausschließlich "für den Eigenbedarf".
Startseite Hardware Rechtliches Kontakt