Nutzung der Phidgets-API (Teil 1: Programmiersprache C)

 
 
Um sich in die Programmierung neuer Konzepte einzuarbeiten, schauen wir uns häufig zunächst komplexe Beispiele an und passen diese dann an unsere Anforderungen an. Dadurch kommt man zwar schnell ans Ziel, aber es ist nicht immer leicht nachvollziehbar, wofür bestimmte Anweisungen verwendet werden. In dieser Tutorial-Reihe werden wir anders vorgehen: Wir beginnen mit der absoluten Minimalversion und erklären dabei jeden Schritt. Dabei werde ich den ersten Teil in C programmieren und in den beiden nächsten Teilen auf C# und Java übergehen. Auch wenn Sie mit der ein oder anderen Sprache nicht vertraut sind, so ist das Konzept gleichbleibend und das Vorgehen übertragbar.
 
Das erste Beispiel erstelle ich in Visual Studio Express 2012. Sie können es aber mit jedem anderen C-Compiler, z. B. BCC32 oder MinGW nachbauen, da es sich um eine simple Konsolenanwendung handelt. Wir wollen dabei nicht anderes tun, als auf ein Phidget-Modul zu warten und dann auslesen, um welches Board es sich handelt. 
 
Dreh- und Angelpunkt: Events
 
Das wichtigste Konzept der Phidget-Kompoenten sind die Events, die ausgelöst werden und auf die unser Programm reagiert. Events werden immer dann gefeuert, wenn sich irgendetwas tut: Ein Phidget-Modul wird eingesteckt, ausgesteckt, der Wert eines Sensors ändert sich etc. Schauen wir uns an, wie das in der Praxis abläuft. 
 
Wir erstellen dazu ein neues leeres Konsolenprojekt und löschen (wenn nörig) den standardmäßig erstellten Programmtext. Jetzt erstellen wir Zeile für Zeile unser erstes Phidgets-Programm, am Schluss können Sie den zusammenhängenden Code sehen. Zunächst brauchen wir 2 #include-Statements:
 
#include "stdafx.h"
 
#include "phidgets.h"
 
Durch die erste Zeile werden vorkompilierte Header eingebunden, das ist spezifisch für den Microsoft-Compiler, die 2. Zeile bindet unsere Phidgets-Funktionen ein. Ich habe die Header-Dateien im aktuellen Source-Code-Verzeichnis, deshalb die Anführungszeichen. Wenn Sie die Header-Dateien in einem Default-Verzeichnis unterbringen, müssen Sie sie in spitze Klammern setzen: #include <phidgets.h>.  Nun aber zum eigentlichen Programm. 
 
 
1. Definition eines Handles
 
In C wird unser Phidget-Board durch eine Struktur im Speicher abgebildet. Um darauf zuzugreifen, brauchen wir einen Zeiger, ein Handle. Das ist der erste Schritt, den wir ausführen:
 
Bild_1
 
Da die Struktur abhängig von der Art des Moduls ist, gibt es unterschiedliche Strukturen und somit auch Handle. Wir arbeiten in diesem Beispiel wieder mit einem InferfaceKit 2/2/2 also brauchen wir eine Struktur vom Typ  "_CPhidgetInterfaceKit", auf die ein Handle vom Typ "CPhidgetInterfaceKitHandle" zeigt. Bei einem PhidgetLED-64 wäre es eine Struktur vom Typ "_CPhidgetLED" auf die ein Handle vom  Typ "CPhidgetLEDHandle" zeigt. 
 
 
 
2. Erzeugung der Struktur
 
Bisher haben wir nur eine Adressvariable "ifKit", die aber noch nirgendwo hinzeigt (Nullzeiger). Das ändern wir im 2. Schritt:
 
Bild_2
 
Dazu rufen wir die Funktion "CPhidgetInterfaceKit_create()" auf und übergeben Ihr die Adresse des Handles. Ab jetzt befindet sich die Struktur im Speicher und wir können damit arbeiten. 
 
 
 
3.  Öffnen des Phidget-Moduls
 
Wir haben eine Struktur im Speicher und einen Zeiger darauf, allerdings gibt es noch keine Verbindung zum Modul selbst. Denn es könnte ja durchaus mehrere Strukturen geben und mehrere physikalische Module. Woher weiß also die Struktur, mit welchem Modul sie verbunden ist? Dafür gibt es die Funktion: CPhidget_Open(CPhidgetHandle phid, int Seriennummer). Als Übergabeparameter gibt es die beiden Verbindungspunkte "Handle" (Struktur im PC) und "Seriennummer" (tatsächliches Modul). Wenn Sie die Seriennummer kennen, können Sie ganz gezielt ein bestimmtes Modul ansprechen, kennen Sie diese nicht, wählen Sie einfach -1 und es wird ein beliebiges Modul geöffnet. 
 
Bild_3
 
 
4.  Verbindung mit dem Event-Handler
 
Wenn ein Phidget-Modul über die USB-Schnittstelle verbunden wird, erzeugt der Treiber ein "OnAttach"-Event. Allerdings bekommt unser Programm davon im Moment noch nicht viel mit. Aus diesem Grund müssen wir 
 
a: eine Funktion definieren (Event-Handler), in der die Aktionen festgelegt werden, die bei diesem Ereignis stattfinden sollen,
b: das Event muss mit diesem Event-Handler verbunden werden.
 
Genau diese Aufgaben übernimmt die nächste Code-Zeile:
 
Bild_4
 
Die Funktion heißt CPhidget_set_OnAttach_Handler() und benötigt 2 Parameter: den Handler der Struktur und einen Zeiger auf die Funktion, die aufgerufen werden soll. Der Name einer Funktion ist nichts anderes als der Zeiger darauf, also übergeben wir hier AttachHandler. Der 3. Parameter (hier NULL) ist ein User-Pointer, der vom Anwender genutzt werden kann und dem Handler übergeben wird. Den benötigen wir hier nicht.
 
Hinweis: Der Name der Funktion kann natürlich  frei gewählt werden, sie muss nicht  "AttachHandler"  heißen. 
 
5. Definieren des Event-Handlers
 
Die Funktion "AttachHandler" müssen wir natürlich dann noch im Programm definieren und das wollen wir jetzt tun:
 
Bild_5
 
 
Beim Verbinden mit dem PC soll der Phidget-Name und die Seriennummer ausgegeben werden. Wir deklarieren dafür zwei Variablen: "serialNo" und "name". Mit CPhidget_getDeviceName wird der Name, mit CPhidget_getSerialNumber die Seriennummer eingelesen. Dazu wird der Phidget-Handle und die Variable übergeben. Danach wird das Ganze über eine printf-Funktion auf der Konsole ausgegeben. 
 
Event-Handler werden in der Regel entweder vom Betriebssystem oder von Treibern aufgerufen. Dazu werden die Parameter in einen bestimmten Speicherbereich gelegt (dem Stack) und dann wird die Funktion gesucht und aufgerufen, die diesem Event zugeordnet ist. Nun gibt es aber unterschiedliche Konventionen, wie die Parameter auf dem Stack abgelegt werden. Bei Win32-API-Aufrufen werden sie in der Reihenfolge von rechts nach links gespeichert. Damit der Event-Handler weiß, in welcher Kovention dies geschieht steht vor dem Funktionsnamen "AttachHandler" das Makro CCONV. Dahinter verbirgt sich nichts anderes als "__stdcall", dem Bezeichner für die Win32-Konvention. 
 
Damit wäre unser Programm im Grunde lauffähig. Wir müssten es nur noch in einer Schleife fangen. Aber wir wollen noch ein Mindestmaß an Eleganz hinzufügen, nämlich einer Time-Out-Funktion, die geduldig 10 Sekunden wartet, ob sich ein Phidget meldet. 
 
Bild_6
 
Die Funktion "CPhidget_waitForAttachment()" tut genau das, was der Name sagt: sie wartet auf das Einstecken eines Phidget-Moduls. Sie wartet aber nicht auf irgendein Modul, sondern auf das Modul, dessen Handle schon geöffnet ist und deshalb wird dieser Handel übergeben. Der 2. Parameter ist die Zeit in Millisekunden.
 
Wird innerhalb von 10 Sekunden nach Programmstart kein Phidget-Modul gefunden (result ungleich 0) springt das Programm in die if-Schleife und ruft die Funktion "CPhidget_getErrorDescription()" auf. Der Fehler-Code entspricht dem Wert von result und daraus ermittelt die Funktion eine Fehlerbeschreibung, die ausgegeben wird. Die beiden getchar()-Anweisungen dienen lediglich dazu, ein Schließen der Konsole zu verhindern. 
 
Damit haben wir das erste Programm fertiggestellt. Im Anschluss noch einmal der komplette Programm-Code:
 
Bild_7
 
 
Wenn Sie das Programm compilieren und aufrufen, sollte das Ergebnis in etwa so aussehen:
 
Bild_8
 
Würden Sie anstelle eines InterfaceKits ein anderes Modul einstecken oder auch zu lange warten, dann erhalten Sie die folgende Fehlermeldung:
 
Bild_9
 
 
Programmieren Sie als kleine Erweiterung doch einen Handler für das Entfernen des Moduls aus. Dazu müssen Sie wiederum eine Funktion schreiben, in der die Reaktion definiert wird (z. B. DetachHandler) und Sie verbinden mit der Funktion CPhidget_set_OnDetach_Handler((CPhidgetHandle)ifKit, DetachHandler, NULL) die Funktion mit dem Event. 
 
Im nächsten Teil gehen wir dann auf die eigentliche Funktionalität des Moduls ein und programmieren und ein Abstandwarnsystem. 
 
 
 
Copyright © Böcker Systemelektronik