Kommunikationsbus für Mikrocontroller

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"

Interface Schaltung
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.
Busprotokoll

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

Schaltplan Serielles Interface
Layout Serielles Interface
Um Daten zwischen einem Mikrocontroller und z.B. einem PC auszutauschen, habe ich ein einfaches Interface zu einer seriellen Schnittstelle erstellt. Diese Schaltung lässt sich ohne eine separate Stromversorgung auch an z.B einem "Seriell/USB-Adapter" betreiben, und ermöglicht den Zugang zu dem beschriebenen Bus z.B. mit einem Terminalprogramm wie z.B. "minicom". So lassen sich relativ einfach Befehle an den Mikrocontroller senden, oder z.B. Debug-Informationen auslesen. Für die Behandlung des CSMA/CD-Protokolls ist jedoch ein speziell angepasstes Programm auf dem PC notwendig. Da die Schaltung relativ einfach ist, habe ich das Layout so gestaltet, daß ein Aufbau auf einer Lochrasterplatte möglich ist.
Serielles Interface aufgebaut


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

HTML und Design: DK1RM erstellt: 12.9.2019 - letzte Änderung: 19.9.2019