Blick ins Labor

Zwar versteht sich das c't-Lab mit jeder Programmiersprache, die eine serielle Schnittstelle bedienen kann - so richtig Freude kommt mit C und Konsorten aber nicht auf, wenn sich die Bedien- und Anzeigenelemente an reale Vorbilder anlehnen sollen und Änderungen im Messaufbau nicht die Ausnahme, sondern die Regel sind. Wir empfehlen deshalb die grafische Programmierumgebung LabVIEW: Damit gelingt die Umsetzung flexibler, schneller und einfacher - wenn auch mit einem durchweg anderen Ansatz.

vorlesen Druckansicht
Lesezeit: 26 Min.
Von
  • Carsten Meyer
Inhaltsverzeichnis

Schon einmal einen Drehknopf, einen skalierbaren Zeitschrieb und ein Zeigerinstrument in C programmiert? Das Listing sieht selbst beim Einsatz vorgefertigter GUI-Libraries so aus, als hätte man „ein Gürteltier über die Tastatur gerollt“ (um einmal eine fast 20 Jahre alte c't-Ausgabe zu zitieren). Änderungen erfordern in der Regel umständliche Anpassungen des Quelltextes - was für „fliegende“ Messaufbauten kaum praktikabel ist.

LabVIEW ist anders. Hier gibt es nichts zu kompilieren, zu assemblieren und zu linken - die Programmierung erfolgt durch „Verdrahtung“ eines Ablaufplans, der entfernt an ein Schaltbild erinnert, aber eher der Gattung „Flussdiagramm“ zuzuordnen ist. Seine Stärken spielt die Entwicklungsumgebung aus, wenn es darum geht, Messergebnisse oder Prozesse ansehnlich darzustellen. Als virtuelle Bedienelemente und Anzeigen gibt es Zeigerinstrumente, Thermometer und Digitalanzeigen, Drehknöpfe und Taster, Oszillogramme, XY-Diagramme und Zeitschriebe - wobei die Konfigurationsmöglichkeiten jeder Display-Variante geradezu uferlos erscheinen.

Wenn Sie die auf der Heft-DVD 11/2007 beiliegende Vollversion von LabVIEW 6.1 noch nicht installiert haben, wird es dafür höchste Zeit, zumal schon der Abgleich einiger c't-Lab-Module ohne die unter www.ct-lab.de verfügbaren LabVIEW-Demos unnötig umständlich gerät. Zugegebenermaßen fällt es gerade gestandenen Programmierern schwer, sich an die LabVIEW-Gepflogenheiten zu gewöhnen - aber da müssen Sie jetzt durch. Erfolgserlebnisse stellen sich im Übrigen deutlich schneller als beim Erlernen herkömmlicher Programmiersprachen ein.

Das Programmpaket ist mit über 400 MByte zwar ein recht dicker Brocken, doch keine Angst: Viele Funktionen und Features werden Sie für den Einstieg nicht brauchen, und auch von den Hilfsprogrammen bleibt nur ein einziges essenzielles übrig. Sie können die Installation direkt aus dem ZIP-Archiv der Heft-DVD erledigen, WinZIP legt bei Bedarf ein temporäres Verzeichnis an. Der Installationsvorgang ist gemessen am Umfang des Pakets überraschend schnell erledigt, wenn man die (empfehlenswerten) Default-Einstellungen übernimmt. Vorher sollten Sie sich aber bei National Instruments eine gültige Seriennummer für die c't-Version besorgen [1].

Nach der Installation und dem Reset des Rechners starten Sie zunächst das mitinstallierte Tool „Measurement & Automation Exporer“ (MAX), das in unserem Fall nur dazu dient, der für das c't-Lab verwendeten seriellen Schnittstelle einen eindeutigen Namen für die LabVIEW-interne Treiberarchitektur (VISA, Virtual Instrument Software Architecture) zuzuweisen und diese zu initialisieren. Das Programm erkennt ebenso Messwerterfassungs- und Interface-Karten von National Instruments sowie angemeldete Netzwerk-Verbindungen zum Fernsteuern und Fernabfragen anderer Labor-Rechner und der daran angeschlossenen Hardware - auch das ist mit LabVIEW möglich.

Klicken Sie links im Geräte-Baum auf „Geräte und Schnittstellen“ und wählen Sie die zu verwendende serielle Schnittstelle aus (auf PCs wird dies in den meisten Fällen COM1 sein). Emulierte COM-Ports, etwa die des c't-Lab-USB- oder XPORT-Ethernet-Anschlusses, sollten ebenfalls in der Liste erscheinen. Dann oben auf „Properties“ klicken (geht auch im Kontext-Menü mit der rechten Maustaste) und im erscheinenden Dialog auf dem zweiten Kartei-Reiter die richtigen Schnittstellenparameter einstellen (38400, 8, none, 1, none). Den VISA-Ressourcen-Namen merken (bei der ersten Zuweisung von COM1 wird hier „ASRL1::INSTR“ stehen) und MAX beenden. Dem Ressourcen-Namen werden Sie immer wieder begegnen: Er dient zur Identifikation der angeschlossenen Messgeräte und der vorhandenen Schnittstellen. Das ist schon deshalb nötig, weil LabVIEW auf verschiedenen Plattformen zu Hause ist - auch jenen, die mit der Angabe COM1 nichts anfangen können.

Ausführliche Dokumentationen finden Sie übrigens in der PDF-Übersicht „Im LabVIEW-Bücherregal suchen“ (LabVIEW-Programmverzeichnis), darunter auch das LabVIEW-Tutorium, das Sie sich in einer ruhigen Stunde zu Gemüte führen sollten, um bei den Grundbegriffen der Programmierumgebung mitreden zu können. Dann bringt Sie auch die sehr umfangreiche Kontext-Hilfe weiter, die Erklärungen zu jeder Funktion bereithält. Unsere Einleitung wird nur rudimentär auf Datentypen und Syntaxelemente eingehen - soweit sie für die c't-Lab-Steuerung von Belang sind. Für die folgenden Beispiele laden Sie sich bitte sämtliche LabVIEW-Demo-Programme von [2] herunter; sie sollten in einem gemeinsamen (aber ansonsten beliebigen) Verzeichnis auf Ihrer Festplatte stehen.

Starten Sie nun LabVIEW und öffnen Sie im Begrüßungsfenster die Datei „CTLAB-Ident.vi“, was das Bedienungspanel des kleinen Programms öffnet. Der Datei-Suffix „.vi“ weist dezent auf die Bezeichnung VI (Virtual Instrument) für alle LabVIEW-Anwendungen hin. Mit dem Menüpunkt „Fenster/Diagramm“ (oder kurz STRG-E) bekommen Sie den zugehörigen Programmplan dargestellt.

Das LabVIEW-Progrämmchen CTLAB-Ident.vi zeigt die grundsätzliche Vorgehensweise zur Kommunikation mit dem c't-Lab: Schnittstelle initialisieren, Abfrage- oder Befehls-String senden und auf Antwort warten.

LabView unterscheidet zwischen dem „Panel“ genannten Ausgabe-/Bedienfenster und dem im normalen Betrieb nicht sichtbaren „Diagramm“ zur Programmierung. Im Diagramm gibt man die Abfolge der Befehle durch Ziehen von Verbindungen zwischen den Funktionsblöcken und den sich hier abstrahiert wiederfindenden Bedien- oder Anzeigeelementen vor. Letztere sind beim Ident-VI allerdings sehr einfach gestaltet, da dies normalerweise nur als Unterprogramm (Sub-VI) aufgerufen wird, wobei sein Panel unsichtbar bleibt. Trotzdem sollten Sie beim Erstellen eigener Sub-VIs nicht schludern, was die sinnvolle Aufteilung der Anzeige- und Bedienfelder angeht - das erleichtert später das Debuggen und Erweitern.

Man „liest“ das Diagramm von links nach rechts, auch die abgebildeten Funktionsblöcke erwarten auf der linken Seite ihre Eingangswerte und geben sie rechts aus. Die grafische Programmiersprache, „G“ genannt, arbeitet streng nach dem Datenfluss-Modell: Ein Funktionsblock (das kann eine einfache UND-Verknüpfung sein, aber auch ein mächtiger Subroutinen-Schaltplan) gibt erst dann seine Ergebnisse weiter, wenn er allen benötigten Input erhalten und komplett abgearbeitet hat. Es ist also eigentlich egal, in welcher Reihenfolge die Funktionsblöcke auf dem Diagramm platziert werden - ihre Abfolge wird einzig und allein von den bestehenden Verbindungen vorgegeben. Der einfacheren Wart- und Erweiterbarkeit halber sollte man sich bei der Gestaltung des Diagramms aber an die im westlichen Kulturkreis übliche Lese-Richtung halten.

Unser erstes Beispiel initialisiert die ausgewählte Schnittstelle (hier kommt der oben erwähnte VISA-Ressourcen-Name zum Zuge), gibt den Abfrage-Befehl „*:IDN?“ an das c't-Lab aus und schreibt die Antworten der Module in ein Ergebnis-Array. Klicken Sie im Panel-Fenster einmal auf den Ausführungs-Rechtspfeil unter der Menüleiste. Wenn im Pop-up-Menü „VISA Ressourcenname“ die Schnittstelle des c't-Lab richtig eingestellt ist, sollten im Array-Feld die Antworten (Modulnamen und Seriennummern) erscheinen. Erfolgt wegen kommunikativer Defizite keine Antwort, leuchtet die virtuelle LED „No Response“ rot auf.

Was im Einzelnen passiert, können Sie im Diagramm nachvollziehen, sobald der „Highlight“-Modus (Glühlampen-Icon unter der Menüleiste) aktiviert ist und Sie das VI nochmals starten: LabVIEW zeigt im Schaltplan nun die Datenbewegungen und die Abfolge der Funktionen durch wandernde Punkte entlang der Verbindungen - sehr hilfreich beim Debuggen eines VI, ebenso wie die „Probe“ der Werkzeugpalette, eine Art Tastkopf für Daten. Das Programm beginnt mit der Übernahme der Parameter aus dem Baudraten- und Ressourcen-Feld, die der erste Funktionsblock „VISA Configure Serial Port“ erhält. Der stellt die Schnittstellenparameter wie gewünscht ein; einige Parameter sind mit reinen Konstanten belegt, die keine Entsprechung auf dem Panel haben. So ist zum Beispiel die Anzahl der seriellen Bits mit acht fest vorgegeben, ebenso wie das Zeilenendezeichen (Linefeed, ASCII dezimal 10). Der folgende Block „I/O-Puffer entleeren“ erhält vom ersten nach dessen Ausführung den Ressourcennamen und eine eventuelle Fehlermeldung übergeben, die er seinerseits an den ersten richtigen Kommunikationsblock weiterreicht. „VISA: Schreiben“ macht genau, was der Name sagt: Einen String auf der im Ressourcennamen angegebenen Schnittstelle ausgeben. In diesem Fall ist der String der c't-Lab-Identifikationsbefehl „*:IDN?“, gefolgt von einem ASCII-CR/LF. Um diese „nichtdruckenden“ Zeichen einzugeben, kann man bei allen String-Anzeige- und Bedienelementen sowie -Konstanten die „\“-Code-Anzeige einschalten (Element mit der rechten Maustaste anklicken, im Kontext-Menü): „\r“ steht für „Return“, „\n“ für „New Line“.

Der seriellen Ausgabe folgt eine sogenannte Sequenz, symbolisiert durch den perforierten Filmstreifen. Alle Funktionen innerhalb einer Sequenz werden erst dann abgearbeitet, wenn gemäß dem Datenfluss-Modell alle Eingangswerte vorliegen - auch wenn diese nur durchzureichen sind. Hier wird einfach ein Weilchen gewartet und damit der Antwort-Latenz Rechnung getragen. Der Delay-Block (Armbanduhr-Symbol) würde, stünde er nicht innerhalb des Sequenz-Kästchens, kaum zum beabsichtigten Zeitpunkt warten, weil er in keinem Datenfluss-Abhängigkeitsverhältnis steht. Schließlich handelt es sich bei LabVIEW um ein Multithreading-System, was bedeutet, das unabhängige Funktionsgruppen (also solche, die nicht auf die Ergebnisse anderer warten müssen) quasi gleichzeitig abgearbeitet werden.

Die nächste Funktionsgruppe ist eine While-Schleife, die die eingehenden Antwort-Strings sammelt. Der VISA-„Eigenschaftsknoten“ oder auch Property Node (zu finden in der Funktionspalette unter „Instrumenten-I/O, VISA, Fortgeschritten“) liefert die Anzahl der eingetroffenen Zeichen im I/O-Puffer. Ist die Anzahl der Zeichen kleiner vier, wird gar nicht erst versucht, von der Schnittstelle zu lesen: Die c't-Lab-Meldung ist garantiert noch unvollständig, und man spart sich so das Ablaufen des Schnittstellen-Timeout. Anderenfalls liest der Block „VISA:Lesen“ die Antworten des c't-Lab von der Schnittstelle, bis keine mehr kommt - die Lese-Funktion also mit einem Timeout abbricht. Gehen gar keine Strings ein, leuchtet die virtuell angeschlossene „No Response“-LED auf dem VI-Panel auf.

Damit die Antworten der Module nicht verloren gehen, ist der Ergebnis-Ausgang der While-Schleife auf Auto-Indizieren gestellt (zu aktivieren im Kontext-MenĂĽ beim Rechtsklick auf den Anschluss). Die Schleife liefert an einem indexierten Ausgang ein eindimensionales Array, wobei jedes Element einem Schleifendurchlauf entspricht. AbschlieĂźend wird der letzte (durch Timeout ungĂĽltige) Eintrag entfernt und das Array in aufsteigender Modul-Reihenfolge sortiert.

Damit man das VI auch sinnvoll als Unterprogramm-Block (Sub-VI) in ein Hauptprogramm einfügen kann, muss man ihm Anschlussfelder zuweisen. Dies erreicht man mit dem Kontext-Menü „Anschluss zeigen“ durch einen Klick in das Icon rechts oben im Panel-Fenster. Anschließend kann man bis zu 28 Anschluss-„Pins“ erstellen und mit den Bedien- und Anzeige-Elementen verknüpfen: Einfach in das gewünschte Anschlussfeld klicken und dann auf das damit zu verbindende Element. Damit das Anschlussfeld nicht allzu unübersichtlich wird, sollte man sich tunlichst auf zehn oder zwölf „Pins“ beschränken und Signale falls nötig zu „Clustern“ zusammenfassen; Eingänge sollten wenn möglich links zu liegen kommen.

Auch wenn es ein bescheidenes Frontpanel besitzt, ist CTLAB-Ident.vi vornehmlich als Sub-VI zur Integration in ein Haupt-programm gedacht - es liefert ein Array mit den Identifikations-Strings der angeschlossenen c't-Lab-Module.

Cluster sind im Prinzip Kabelkanäle mit einer fast beliebigen Anzahl von Einzeladern (Werte, Strings) oder auch „mehrpoligen“ Kabeln (Arrays, Sub-Cluster). Machen Sie von den Clustern regen Gebrauch - das Bündeln und Spleißen kostet keine Rechenzeit, aber das Diagramm gewinnt merklich an Übersichtlichkeit. Nicht benötigte Sub-VI-Ausgänge lässt man einfach offen, während nicht angeschlossene Eingänge mit den im Sub-VI-Panel vorbelegten Daten beschickt werden. Die kann man übrigens dauerhaft ändern, indem man im Menü „Ausführen“ den Befehl „Aktuelle Werte als Standard“ auswählt; dies ist zum Beispiel für den Ressourcennamen der Demo-Programme hilfreich, den man sonst vor jedem Start neu zuweisen müsste.

Nicht nur aus ästhetischen Gründen empfiehlt die LabVIEW-Dokumentation, gleichartige Signale (z. B. Ressourcennamen oder Fehlermeldungen) immer auf gleicher Höhe des Anschlussfeldes unterzubringen, damit kein virtueller Drahtverhau entsteht. Ein Sub-VI mit zugewiesenem Anschlussfeld lässt sich sehr einfach in ein anderes Programm integrieren: Einfach aus dem Windows Explorer auf das Haupt-Diagramm ziehen oder mit „Wählen Sie ein VI...“ links unten in der Funktionspalette. Das DemoAll-VI verwendet beispielsweise das vorgestellte Ident-VI zur Initialisierung der Schnittstelle. Sie finden es im DemoAll-Diagramm links oben.

Das Frage-Antwort-Spiel des Ident-VI übernehmen auch CTLAB-SendVal.vi und CTLAB-ReceiveVal.vi, nur dass sie lediglich auf die Antwort eines einzelnen Moduls warten und auf die Initialisierung verzichten. Bei SendVal, das einen Einstellwert an das c't-Lab sendet, ist dies nur die „OK“-Rückmeldung des Modul-Controllers, während ReceiveVal einen Messwert anfordert und diesen bei Erhalt nach Moduladresse, Subkanalnummer und Wert aufschlüsselt. Beide VIs basteln zunächst aus Modul- und Kanalnummer und String-Fragmenten den Befehl zusammen, schicken ihn an die Schnittstelle und warten so lange, bis ein Messwert eintrifft - entsprechend der c't-Lab-Syntax (siehe www.ct-lab.de) erkennbar am Präfix-„#“. Trifft bis zum Timeout keine Antwort mit passenden Modul- und Kanalnummern ein, enthält der Error-Cluster eine Fehlermeldung. Neben dem nackten Messwert liefern die VIs auch einen Ergebnis-Cluster, dessen wichtigster Bestandteil das „Valid“-Flag ist - es zeigt an, ob ein Messwert korrekt empfangen oder der Befehl ohne Fehlermeldung akzeptiert wurde. Zusätzlich enthalten ist der komplette Empfangs-String, so wie er an der seriellen Schnittstelle eingegangen ist. Da diese VIs keine Schnittstellen-Initialisierung vornehmen, sind sie nur eingeschränkt autark: Bevor sie gestartet werden, muss die Schnittstelle wenigstens einmal (etwa durch CTLAB-Ident.vi) initialisiert worden sein.

Einfachere c't-Lab-Anwendungen kommen bereits mit den drei vorgestellten VIs gut aus: Zum Programmstart einmal mit CTLAB-Ident initialisieren und in der Programm-Hauptschleife Werte mit SendVal setzen und/oder mit ReceiveVal abfragen. Unser kostenloses LabVIEW 6.1 unterstützt leider nur begrenzt die Event-orientierte Programmierung, sodass eine auf Bedienung seitens des Anwenders oder Werteänderungen seitens der Messaufnehmer wartende Programm-Hauptschleife immer notwendig ist - von simplen Einmal-Berechnungen abgesehen, die keine Benutzer-Interaktion erfordern.

Einige unserer Abgleich-Programme verwenden der Einfachheit halber diesen simplen Send-Receive-Mechanismus. Voraussetzung ist allerdings, dass immer nur ein c't-Lab-Modul gleichzeitig angesprochen wird - sonst kommt die Empfangsroutine des Programms ins Straucheln: Wenn beispielsweise ein Modul von sich aus Messwerte liefert, weil die Messung per Trigger-Signal und nicht per Befehl angestoßen wurde, ist die zeitliche Abfolge von zusammengehörenden Fragen und Antworten nicht mehr gewährleistet. Das ReceiveVal-VI erhält dann eine unpassende Kanalnummer zugespielt, ignoriert sie und der Messwert geht verloren. Schlimmstenfalls kann in einer „engen“ Schleife bei kleinen Übertragungsfehlern die Synchronisation sogar ganz verloren gehen, wenn etwa die Sende-Routine anstatt eines „OK“-Prompts einen Messwert erhält und die Messwertabfrage die Statusmeldung.

Schon bei leicht gestiegenen Anforderungen an die Zuverlässigkeit ist das SendVal-/ReceiveVal-Pärchen überfordert. Besser, man entkoppelt Sende- und Empfangsschleife voneinander, das heißt, man wartet nach dem Senden eines Befehls nicht, bis ein Messwert oder ein OK eintrifft, sondern sammelt eingehende Messwerte in einer getrennten, unabhängig vom Hauptprogramm laufenden Schleife. Asynchronitäten sind damit von vornherein ausgeschlossen - man muss nur darauf achten, dass die gegebenenfalls bunt gemischt eingehenden Messwerte von den c't-Lab-Modulen richtig zugeordnet werden können.

Die einfachste Möglichkeit, mit dem c't-Lab zu kommunizieren, bieten die ReceiveVal- und SendVal-Sub-VIs. Allerdings können Messwerte verloren gehen, wenn extern getriggerte Messwerte dazwischenfunken.

Das wird durch die voll „adressierten“ Messwert-Telegramme der c't-Lab-Syntax extrem vereinfacht. Jeder eingehende Messwert lässt sich dank der vorangestellten Modul- und Kanalnummer eindeutig zuordnen. Man muss aus dem Empfangs-String nur die richtigen Ziffern und Zahlen extrahieren - was nicht besonders schwer fällt, da Messergebnisse und Status-Antworten immer nach dem Schema „#:=“ aufgebaut sind. Hat man Modul- und Subkanalnummer destilliert, legt man den Messwert einfach in einem zweidimensionalen Array ab, indexiert nach Moduladressen (8 Zeilen) und Subkanalnummern (256 Spalten). Um einen angeforderten Messwert zu erhalten, reicht es, nach Ablauf der Mess-Latenzzeit (üblicherweise weniger als 20 ms) im Ergebnis-Array nachzuschauen.

Die Zuverlässigkeit dieses Verfahrens ist ungleich größer als die oben beschriebene Send-Receive-Prozedur, so lange die Empfangsschleife nur schnell genug Daten abholt. Das ist ohne weiteres gewährleistet, wenn ihr keine weiteren Aufgaben übertragen werden und im Hauptprogramm nicht vergessene Rechenzeitfresser lauern - etwa eine auf Höchstdrehzahl leer laufende Endlos-Schleife ohne implizite (z. B. Read-Funktion mit Timeout) oder explizite (z. B. Delay-Funktion) Bremsen. Delay- und Timeout-Funktionen haben in LabVIEW (zumindest noch in der Version 6.1) eine wichtige Aufgabe - sie sorgen inhärent für die gerechte Aufteilung der Rechenzeit unter den einzelnen Programmschleifen. So lange Sie nicht auf das Event-orientierte LabVIEW 8.x upgraden, braucht jede Endlosschleife auch eine Delay-Funktion. Bei Schleifen, die sich um Benutzeraktivitäten kümmern, verwendet man zweckmäßigerweise die Funktion „Auf Frontpanel-Aktivität warten“ (unter „Zeit & Dialog“ in der Funktionen-Palette) mit einem moderaten Timeout (200 ms), was noch angenehm schnelle Anzeige-Updates liefert.

Da Arrays in LabVIEW ebenso wie andere Variablen nichtpersistent sind, also praktisch nur zwischen Erzeugung und Dereferenzierung/Auswertung existieren, muss man sich eines kleinen, aber durchaus salonfähigen Tricks bedienen, um das Messwerte-Array auch anderen Programmteilen außerhalb der Empfangsschleife zugänglich zu machen. Man legt in der Empfangsschleife ein (in LabVIEW etwas missverständlich sogenanntes) Schieberegister an, das den Wert eines beliebigen Daten-Containers bis zum nächsten Schleifendurchlauf aufbewahrt. Den Rest der Arbeit - also das Aufschlüsseln und Speichern der eingehenden Messwerte in ebendieses Array - übernimmt unser Sub-VI CTLAB-ReceiveArray.vi. Falls kein Zeichen zur Auswertung anliegt, führt das VI ein Delay von 5 ms aus, damit genügend Rechenzeit für andere LabVIEW-Prozesse zur Verfügung steht.

Das Demo-Programm DemoAll.vi macht von CTLAB-ReceiveArray.vi Gebrauch: Damit das von der Empfangsschleife ständig aktualisierte Ergebnis-Array andernorts zur Verfügung steht, gelangt es auf ein Array-Anzeigeelement, das im Diagramm aber unbedingt innerhalb der Schleife platziert sein muss. Läge es außerhalb, würde es nur beim Beenden der Schleife aktualisiert - was bei Endlosschleifen naturgemäß dauern kann. In anderen Programmteilen kann man dann auf das (hier unsichtbar gemachte und damit auf der „Frontplatte“ nicht störende) Array-Anzeigeelement über eine lokale Variable zugreifen. „Lokal“ deshalb, weil sie außerhalb des umgebenden (Haupt-)VI nicht verfügbar ist - im Unterschied zu den eher umständlichen und sehr langsamen globalen Variablen, die man nur im Notfall benutzen sollte.

Lokale und globale Variable widersprechen im Prinzip dem bei LabVIEW propagierten Datenfluss-Modell, sind aber manchmal (wie im vorliegenden Fall) unvermeidlich, wenn Daten zwischen unabhängig nebeneinander kurrenten Programmteilen ausgetauscht werden müssen. Sie bergen gewisse Risiken, wenn zwei Prozesse gleichzeitig versuchen, schreibend darauf zuzugreifen - das Ergebnis ist dann ungewiss. Im vorliegenden Fall ist die Verwendung aber genehm, weil nur die Empfangsroutine ins Array schreibt.

Verwendet man LabVIEW-Notify-Messages, ist die quasi gleichzeitige Kommunikation mit verschiedenen c't-Lab-Modulen kein Problem. Das InitNotify-Sub-VI enthält eine Empfangsschleife, die Meldungen vom c't-Lab in Notifier „umrechnet“ und an das Hauptprogramm sendet.

Das CTLAB-ReceiveArray.vi ist überaus einfach anzuwenden und lässt sich in jede LabVIEW-Applikation integrieren, ohne dass es zu störenden Interferenzen mit anderen Programmteilen kommt. Wie erwähnt muss aber jeder benötigte Messwert explizit angefordert werden, es sei denn, das c't-Lab liefert durch automatische (zeitgesteuerte) oder externe Triggerung von sich aus Messwerte - auch die werden selbstverständlich im Array abgelegt. Das Anfordern geschieht mit CTLAB-RequestVal.vi, während die Modul-Parameter und Ausgabewerte mit CTLAB-SetVal.vi gesetzt werden können (siehe auch DemoAll.vi). Beide VIs warten im Unterschied zu ReceiveVal und SendVal nicht auf einen OK-Status oder einen Messwert, sie setzen lediglich den entsprechenden Befehl ab. Auch die Status- und Fehlermeldungen landen im Ergebnis-Array: auf allen Kanal-Reihen in Spalte 255, dem Status-Subkanal.

Dazu gleich eine wichtige Anmerkung: Mit Version 1.6 der ADA-IO-Firmware sowie bei allen anderen folgenden c't-Lab-Modulen wurde das Prompt-Verhalten etwas geändert, um den Datenverkehr auf dem c't-Lab-internen OptoBus reduzieren zu können. Sendet man einen Parametrierungsbefehl ohne das abschließende „!“, etwa „0:20=5.0“, wird er vom adressierten c't-Lab-Modul ohne Bestätigung durch die bandbreitenfressende Bestätigungsmeldung (hier „#0:255=0 [OK]“) übernommen.

Das Demo-Programm NotifyDemo zeigt den Einsatz unabhängiger Programmschleifen, die erst mit den LabVIEW-Notifiern möglich werden. Quasi gleichzeitig fordert es Messwerte an, gibt auf einem D/A-Kanal eine Sinusschwingung aus und zeigt einen Messwert auf dem Zeigerinstrument.

Wenn es dagegen auf Übertragungssicherheit ankommt, kann dem Befehl eine XOR-Prüfsumme (8 Bit) in hexadezimaler Schreibweise angehängt werden. Ein $ mit der XOR-Sum-me über den gesamten Befehls-String (bis zum letzten Zeichen vor dem „$“), also zum Beispiel „0:VAL 20=1.234!$45“, fordert das jeweilige Modul auf, die übergebene Prüfsumme gegen die errechnete zu checken und im Fehlerfall mit dem (neuen) Fehlercode 7 abzubrechen. Ein echtes CRC-16 würde den Controller leider arg lange beschäftigen. Die XOR-Prüfsummenberechnung unter LabVIEW erledigt XOR8.vi. Ohne „$“ erfolgt wie früher keine Prüfsummenberechnung, Zeichen hinter dem „!“ werden dann - so vorhanden - einfach verworfen. In den Beispiel-VIs zum Schnittstellen- und Performance-Test (COMtest.vi, COMperformance.vi) kann man die Prüfsummenberechnung probeweise einschalten, die anderen Demo-Programme verwenden sie nicht.

CTLAB-ReceiveArray.vi enthält mit dem (optional verwendbaren) „Occurence“-Anschluss eine einfache Möglichkeit festzustellen, ob neue Messwerte eingetroffen sind. LabVIEW-Occurences (in der Funktionspalette unter „Fortgeschritten, Synchronisierung“ zu finden) sind Melder, die bei Zutreffen einer Bedingung, beispielsweise in einer Case-Struktur, gesetzt werden und anderenorts abgefragt werden können. Leider ist der zugrunde liegende Mechanismus nicht besonders glücklich dokumentiert, deshalb hier etwas ausführlicher: Die Funktion „Occurence erzeugen“ (in DemoAll.vi-Diagramm links unten, das unscheinbare Kästchen mit dem Kreis) liefert lediglich eine Referenznummer, erzeugt aber nicht das Ereignis selbst. Das geschieht mit „Occurence setzen“, in unserem Fall im ReceiveArray-Sub-VI bei Erhalt eines Messwertes. Der Setz-Vorgang des Melders lässt sich überall im Haupt-VI mit der Funktion „auf Occurence warten“ beobachten, wenn letztere an die erwähnte Referenznummer „angeschlossen“ ist. Der „Auf Occurence warten“-Funktion kann ein Timeout-Wert übergeben werden: Tritt die Meldebedingung nach Ablauf nicht ein, geht der Ausgang der Funktion auf „true“, ansonsten stoppt die Ausführung. Im DemoAll-VI dient er zum Ansteuern des „Response“-Anzeigers: Gehen 250 ms lang keine Antworten vom c't-Lab ein, verlischt die virtuelle LED. Das Melder-Prinzip kann auch dazu dienen, Abfragen und Antworten zu synchronisieren, wie es in der DDS-Demo realisiert wurde: Hier wartet die Sweep-Schleife auf den Messwert vom Millivoltmeter-Zusatz des DDS-Moduls. Bei mehreren an den gleichen Melder angeschlossenen „auf Occurence warten“-Funktionen ist zu beachten, dass nur einer auf „Vorhergehendes ignorieren“ stehen darf (Default, ggf. False-Konstante am Eingang der Funktion anschließen).

Ein Problem bleibt aber dennoch bestehen: Jeder eingehende Messwert kann die Meldung auslösen, nicht nur der gerade angeforderte. Funkt ein c't-Lab-Modul mit einem getriggerten Messwert dazwischen (oder ein von einem anderen unabhängigen Programmteil generiertes Anfrage-Ergebnis), meldet das Konstrukt eine erfolgreich abgeschlossene Messung, auch wenn das eigentlich erwartete Resultat noch gar nicht eingetroffen ist. Mit den Occurence-Funktionen kommt man hier also nicht weiter.

Die Notify-Demo verwendet extensiv LabVIEW-Notifier - der überschaubare Overhead rechnet sich schon mit der zweiten unabhängigen Abfrage-Schleife. Das VI bietet sich als universelles Grundgerüst für eigene Entwicklungen an.

Eine Lösung bieten die LabVIEW-Notify-Messages, die ähnlich funktionieren wie die simplen Occurences - nur dass die Meldung neben dem „ist passiert“ auch einen kompletten Datensatz enthalten darf. Bringt man in diesem die Moduladresse und die Subkanalnummer des empfangenen Messwerts unter, die ja wie oben erwähnt in den Antworten des c't-Lab-Protokolls enthalten sind, kann jedes Programmteil feststellen, ob der eingegangene Messwert auch der eigenen Abfrage entspringt oder doch für andere bestimmt ist.

Dieses elegante Verfahren wurde im NotifyDemo-VI angewendet. Es liefert in drei unabhängigen Schleifen ständig Parameter an das c't-Lab, unter anderem eine (langsame) Sinus-Funktion sowie den Wert eines Drehknopfes, und fragt Messwerte von unterschiedlichen Subkanälen an. Eine Hauptschleife teilt die empfangenen Messwerte nach Subkanalnummern auf und leitet sie an drei Auswerteschleifen weiter - die bekommen tatsächlich nur dann eine Meldung, wenn diese auch mit dem angeforderten Subkanal übereinstimmt. Trotzdem stehen die Messwerte jederzeit im Message-Array zur Verfügung, das parallel zu den Notify-Messages „mitläuft“.

Notify-Melder benötigen bei ihrer Initialisierung die Datenstruktur, die sie übertragen sollen; in unserem Fall ein Cluster mit Moduladresse, Subkanalnummer (beides Integer) und den eigentlichen Messwert (Gleitkomma). Es reicht, ihnen eine Cluster-Konstante mit Nullen zu übergeben. Das Sub-VI CTLAB-InitNotify.vi erledigt dies nicht selbst, weil es eine Endlosschleife zum Empfang der Daten von der seriellen Schnittstelle enthält und demzufolge nie beendet wird; eine intern vergebene Notify-Referenznummer würde folglich nie nach außen, sprich ins Hauptprogramm, gelangen.

Der Subkanal-Dispatcher von NotifyDemo.vi verwendet seinerseits Notify-Messages, um anderen Programmteilen zugedachte Meldungen zu schicken. Die können dann völlig unabhängig voneinander auf eintreffende Messwerte warten; wie die Occurence-Funktion akzeptieren sie einen (optionalen) Timeout-Parameter, womit man der umgebenden Schleife nicht unbedingt eine Delay-Funktion spendieren muss. Der Timeout sollte aber nicht zu niedrig angesetzt werden; bei „0“ rasselt die Schleife mit Höchstgeschwindigkeit durch und verschwendet, wie eingangs erwähnt, unnötig Rechenzeit. NotifyDemo.vi kann als universelles Framework auch für komplexere Messaufgaben herhalten, da es beliebig skalierbar ist.

[1] http://digital.ni.com/express.nsf/bycode/ct_registrierung

[2] www.heise.de/ct/projekte/machmit/ctlab/wiki/LabViewDemos

Forum zu c't-Lab (cm)