Serielle Kommunikation am Beispiel für 6B-Module

Das »Serial Toolkit« aus der Reihe »RealTime Suite« bietet eine einfache und zugleich komfortable Möglichkeit, Sende- und Empfangs-Aufträge für die serielle Schnittstelle zu realisieren. Je nach der konkreten Anwendung kann dabei zwischen verschiedenen Modi gewählt werden. Diese unterscheiden sich vor allem darin, wie der Abschluss einer Datenübertragung signalisiert wird.

So reicht beispielsweise beim Empfangen von Daten der einfache ‘wait-mode’ in den meisten Fällen aus, wenn auf das Eintreffen einer bestimmten Zahl an Zeichen gewartet werden soll. Das Senden von Daten kann mit dem ‘event-mode’ als Hintergrund-Aktivität erfolgen, so dass der normale Programmablauf sofort weitergeführt werden kann.

Im folgenden Beispiel wird dargestellt, wie die in der Messtechnik weit verbreiteten 6B-Module aus dem Hause Analog Devices mithilfe des »Serial Toolkits« angesteuert werden können. Hier verzichten wir aus Platzgründen auf jegliche Fehlerauswertung, was Sie in Ihren Programmen natürlich nicht tun sollten.

Als erstes ist immer die jeweilige COM-Schnittstelle zu öffnen. Dabei wählen wir aus, ob welche Treiber-Implementierung wir wählen: Bei der "normalen" Anwendungs-Implementierung wird das Flag KSF_USER_EXEC angegeben, während mit KSF_KERNEL_EXEC die Kernel-Implementierung gewählt wird, was aber nur beim »Serial Toolkit Plus« zur Verfügung steht. Diese hat den Vorteil, dass serielle Kommunikation in Echtzeit realisierbar ist und sie bietet auch mehr Möglichkeiten, z.B. die Anmeldung von Byte-Handlern. In beiden Fällen wird ein Handle zur Identifizierung der Schnittstelle bei den weiteren Funktionen zurückgeliefert:

Handle hComm;
err = KS_openSerialPort(&hComm, "COM1", "19200,n,8,1", KSF_KERNEL_EXEC);

Bei erfolgreichem Öffnen ist die COM1-Schnittstelle mit den angegebenen Einstellungen für Übertragungsrate, Parität, Daten- und Stop-Bits initialisiert worden.

Der Treiber des Toolkits überwacht von diesem Zeitpunkt an im Hintergrund die Schnittstelle und speichert alle bereits eintreffenden Zeichen in einem eigenen Puffer. Später gestartete Empfangs-Aufträge können diese zwischengespeicherten Zeichen wahlweise berücksichtigen oder verwerfen. Vor dem Empfang von Messdaten (zum Beispiel von einem 6B-Analog-Eingangs-Modul) kommt jedoch das Senden des Anforderungskommandos. Sehr einfach geht das im Hintergrund. Als Parameter ist die Adresse der zu sendenden Zeichenkette anzugeben.

Char* pXmitCmd = "#01\r"; // 01 ist die 6B-Modul-Nr.
err = KS_xmitPort( hComm, NULL, pXmitCmd, 0, NULL, KSF_DONT_WAIT );

Mit dem NULL-Handle als Signalisierungsangabe und dem Flag KSF_DONT_WAIT wird der ‘post-mode’ eingestellt. Die Anzahl zu sendender Zeichen muss nicht angegeben werden – bei einer mit einem Null-Byte abgeschlossenen Zeichenkette ermittelt der Treiber die Länge automatisch.

Beim Post-Modus kehrt die Funktion, die den Sende-Prozess auslöst, sofort zurück. So lassen sich bereits die nächsten Befehle des Programmes bearbeiten, während im Hintergrund das Toolkit dafür sorgt, dass die Bytes tatsächlich gesendet werden.

Als nächstes sollen nun die Zeichen empfangen werden, die den angeforderten Messwert enthalten. Die einfachste Möglichkeit ist der ‘wait-mode’. Dabei kehrt die Funktion zum Start des Empfangs-Auftrages erst zurück, wenn die Zeichen vollständig eingetroffen sind (oder ein Fehler auftrat, was wir hier nicht hoffen).

Zur Übergabe der Zeichen ist ein ausreichend großer Puffer bereitzustellen. Außerdem ist die Anzahl zu empfangender Zeichen anzugeben – hier sollen es 9 sein, denn das Format der 6B-Modul-Daten wurde als “percent of full scale” konfiguriert.

Char pRecvBuf[ 10 ]; // Empfangspuffer
err = KS_recvPort( hComm, NULL, pRecvBuf, 9, NULL, 0 );

Hier einmal folgendes zur Erklärung: Der zweite Parameter bestimmt, auf welchem Wege der Anwender vom Abschluss des Sende- oder Empfangsauftrages zu informieren ist. Dies kann ein Event- oder Callback-Handle sein oder auch ein NULL-Handle, was bedeutet, dass die Funktion selbst blockiert sein soll, bis der Auftrag abgeschlossen ist. Es sei denn, zusätzlich zum NULL-Handle wurde das Flag KSF_DONT_WAIT angegeben: In diesem Fall kehrt die Funktion sofort zurück und es findet keinerlei Signalisierung statt.

Nach erfolgreichem Empfang befinden sich die Zeichen im Format ">+DDD.DD\r" im Empfangspuffer, wobei jedes "D" für eine Ziffer steht und das "+" dem Vorzeichen entspricht.

Verfeinerungen

Das war‘s eigentlich schon! Nun wollen wir uns noch einigen Verfeinerungen widmen.

Beim gerade dargestellten Empfangs-Funktionsaufruf wurden auch die Zeichen berücksichtigt, die bereits vorher vom Treiber intern zwischengespeichert worden waren. Man muss sich also nicht darum sorgen, eventuell ein paar Zeichen zu verpassen und kann daher die Zwischenzeit sinnvoll nutzen.

Um die intern gepufferten Zeichen stattdessen zu verwerfen und damit nur diejenigen auszuwerten, die nach dem Auslösen des Empfangs-Auftrages eintreffen, kann das Flag KSF_CLEAR_BUFFER benutzt werden, um damit zunächst alle zwischengespeicherten Bytes vollständig zu löschen.

Weiterhin lässt sich ein Zeiger auf eine Integer-Variable übergeben, um im Fehlerfall feststellen zu können, wieviele Zeichen tatsächlich empfangen wurden (vielleicht fehlt ja nur das letzte Zeichen, auf das man gerne verzichten kann).

Kommen wir nochmal zum Senden: Das Ende der Datenübertragung erfahren wir im oben gezeigten Beispiel nicht. Wollte man jedoch über das erfolgreiche Ende des Sendevorganges informiert werden, kann zum Beispiel ein Event erzeugt werden, auf das später – am besten in einem eigenen Thread – gewartet wird. Zunächst erzeugen wir das Event-Objekt und erhalten dafür ein Handle:

Handle hXmitEvent;
err = KS_createEvent( &hXmitEvent, "MyTransmitEvent", 0 );

Dann blockiert man einen Thread, indem man auf dieses Event wartet. Dabei lässt sich ein TimeOut-Wert angeben, nach dessen Verstreichen das Warten abzubrechen ist. Soll unendlich lange gewartet werden, ist als ‘timeOut’ 0 anzugeben:

UInteger timeOut = 200*ms;        // maximale Wartezeit
err = KS_waitForEvent( hXmitEvent, 0, timeOut );

Im ‘event-mode’ wird der Thread solange blockiert, bis die Datenübertragung – hier das Senden – abgeschlossen, ein Fehler aufgetreten oder die Timeout-Zeit abgelaufen ist.

Nach Abschluss der Datenübertragungen über eine serielle Schnittstelle sollte diese wieder geschlossen werden:

err = KS_closePort( hComm );

Dadurch steht diese COM-Schnittstelle auch anderen Anwendungen wieder zur Verfügung.

Neben den beiden hier beschriebenen Mechanismen bietet das »Serial Toolkit« noch weitere Übertragungs-Modi an. Auf deren Basis lassen sich auch komplexere Anforderungen erfüllen.

Beim ‘callback-mode’ wird zum Beispiel bei Erreichen der vorgegebenen Zahl zu übertragender Zeichen eine anwenderdefinierte Callback-Routine gerufen. Dieser Modus lässt sich mit dem ‘Unendlich-Betrieb’ kombinieren: Dabei wird der Inhalt des angegebenen Sendepuffers immer wieder von vorn beginnend gesendet, nach jedem Sendezyklus wird die Callback-Routine gerufen. Diese könnte zum Beispiel den Datenteil eines größeren zyklisch zu sendenden Telegrammes aktualisieren.

Zusätzlich lassen sich verschiedene spezielle Handler (meist Callback-Funktionen) anmelden, die bei bestimmten Ereignissen gerufen werden: Bei jedem empfangenen oder gesendeten Byte, bei Übertragungs- oder Timeout-Fehlern, bei Änderungen an den Handshake-Leitungen (LSR/MSR) oder auch bei jedem Hardware-Interrupt. Dadurch kann man zum Beispiel jedem eintreffenden Datenbyte einen “Zeitstempel” vergeben oder auf ein bestimmtes Zeichen (z.B. ein “Token”) sofort reagieren – mit dem Senden einer passenden Antwort, mit dem Aufruf einer speziellen Bearbeitungsfunktion oder auch mit dem Abbruch des Empfangs. Auf diese Weise lassen sich auch komplizierte Übertragungsprotokolle umsetzen.

Fazit

Die genannten Szenarien beschreiben einige typische Aufgabenstellungen bei der seriellen Kommunikation in der Industrie, die mithilfe des »Serial Toolkits« sehr komfortabel lösbar sind. Auch in nahezu allen anderen Fällen wird es möglich sein, einen passenden Übertragungs-Modus des Toolkits mit den zusätzlichen Funktionen zur Status-Überwachung und Steuerung anwenden zu können, um auf diese Weise solche Aufgaben einfach und schnell mit wenigen Funktionsaufrufen realisieren zu können.

Zurück