Volle Kontrolle mit PiControl

In den vergangenen Newslettern ist sein Name oft gefallen und trotzdem haben wir uns eine genauere Erklärung bis heute aufgehoben: PiControl (kurz „PiCon“), unser zentraler Treiber im RevPi Core. Er ist quasi das Herzstück der Software, denn nur über PiCon können Anwendungen Daten mit dem zentralen Prozessabbild austauschen. Aber PiCon bietet mehr als diese Schnittstelle:

  • Bereitstellung von 4 kByte Speicher für die Prozessdaten
  • Bereitstellung eines Device-Treibers, über den mit Standard Linux
  • Dateizugriffen in diesen Speicher geschrieben oder daraus gelesen werden
    kann

  • Bereitstellung eines Funktionsaufrufs, der die mit PiCtory festgelegte
    Systemkonfiguration als Struktur übergibt
  • Erkennung der über PiBridge erreichbaren Module (I/O, Gateways)
  • Konfiguration der I/O-und Gateway-Module über die PiBridge
  • Vergleich der vorgefundenen Modulzusammenstellung mit der PiCtory
    Konfiguration

Wenn beim Starten des Systems die Datei piControl.ko ausgeführt wird, dann werden folgende drei Komponenten geladen: RS485 Kommunikation, Ethernet Kommunikation und Applikationsschnittstelle.

Treiber für zyklischen Austausch von Daten über den RS485 Kanal der PiBridge (z. B. bei I/O-Modulen)

Dieser Treiber führt beim Hochstarten zusätzlich einen Scan auf der PiBridge aus und erkennt welche Geräte dort angeschlossen sind und an welcher physikalischen Position. Das ist wichtig, damit zum Beispiel bei der Verwendung von mehreren I/O-Modulen die Daten am korrekt zugeordneten Platz im Prozessabbild landen, der zuvor von dem Konfigurator PiCtory festgelegt wurde. Deshalb liest PiCon (über seine dritte Komponente) auch beim Starten die vom PiCtory erzeugte JSON-Datei und erstellt daraus eine Konfigurations-Struktur, welche mit der tatsächlich vorgefundenen Zusammenstellung der Module verglichen wird. Nur wenn beide übereinstimmen läuft die zyklische Kommunikation zwischen RevPi Core und den I/O-Modulen an. Andernfalls wird ein Fehlerstatus generiert und die Fehler-LED am RevPi Core blinkt rot. Bei Übereinstimmung werden die gefunden I/O-Module mit den für sie bestimmten Konfigurationsdaten versorgt, die ebenfalls aus der vom PiCtory erzeugten JSON-Datei stammen. Das physikalisch jeweils am Ende der linken und am Ende der rechten Seite der Modulkette angeordnete I/O-Modul bekommt abschließend noch ein Kommando, seinen RS485 Terminierungswiderstand einzuschalten. Danach beginnt der zyklische Datenaustausch über den RS485-Kanal mit einer Zykluszeit, die von der Menge der Datenkommunikation abhängt aber immer maximal schnell ist. Die dafür verwendeten Offsets im Prozessdatenspeicher (also der Platz, von wo Daten kommen oder wohin sie geschrieben werden) kennt der Treiber aus der eingelesenen Konfigurationsstruktur. Um die Datenlast auf dem RS485-Kanal der PiBridge zu verringern (die neuen Zählerfunktionen und PWM-Möglichkeiten der digitalen I/O-Module bringen erheblich höhere Datenmengen mit sich) arbeiten wir übrigens aktuell daran, mehr Intelligenz in diese zyklische Übertragung zu bringen: Daten werden nur übertragen, wenn es inhaltliche Änderungen zur letzten Übertragung gibt: Also Reduktion der Redundanzen.

Die Funktionen dieses Treibers sind nur für den internen Gebrauch gedacht und nach außen für andere Anwendungen nicht zugänglich.

Treiber für den zyklischen Austausch von Daten über die Ethernet Kanäle der PiBridge (z.B. bei Gateways)

Dieser Treiber hat ebenfalls auf die oben erwähnte Konfigurationsstruktur Zugriff und weiß daher, mit welchen Adressen im Prozessabbild er den zyklischen Datenaustausch des jeweiligen Gateway-Moduls durchführen soll. Die Daten aus dem Gateway werden dann unstrukturiert im ganzen Block von der Ethernet-Schnittstelle direkt in das Prozessabbild geschrieben. In der Gegenrichtung werden die Daten ebenfalls im Block aus dem Prozessabbild gelesen und auf die Ethernet-Schnittstelle an das zugehörige Modul gesendet. In einer späteren Version planen wir, dass die Daten im Prozessabbild zwar im Block abgelegt, aber dennoch für Anwendersoftware strukturiert im Zugriff sind. Dafür kannst Du dann im PiCtory Konfigurator symbolische Namen für einzelne Teile des Blocks (also zum Beispiel für ein einzelnes Byte) anlegen. Diese Namen und dazugehörige Offset- und Längenangaben im Prozessabbild können Anwenderprogramm (wie die Soft-SPS logi.CAD 3) verwenden, um gezielt auf nur einzelne Daten aus dem Gateway-Block zuzugreifen. Die Funktionen dieses Treibers sind nur für den internen Gebrauch gedacht und nach außen für andere Anwendungen nicht zugänglich.

„/dev/piControl0“ Linux Device-Treiber als Schnittstelle für Anwendungsprogramme und Treiber von Anwendern bzw. Drittanbietern

Ein konkreter Beispielcode soll Dir zeigen, wie einfach ein Anwendungsprogramm oder Treiber auf das Prozessabbild zugreifen kann. Um 2 Bytes am Offset 10 zyklisch zu lesen:


#include “piControl.h” // This file is provided by KUNBUS
#include // This file is Linux standard driver for file access

int file_hd;
unsigned short inputdata; // This variable is just an example for a
//single data item to be cyclically read from process image

file_hd = open(PICONTROL_DEVICE, O_RDWR); // Name of file has to be set to „/dev/piControl0“

while (…) // This is your main loop with cyclical operations to be done
{
lseek(file_hd, 10, SEEK_SET); // 10 is just an example!
// use offset address in process image
// derived from JSON file by another function
read(file_hd, &inputdata, sizeof(intputdata));
doSomething(inputdata);
}
close(file_hd);

Die Funktion „write()“ funktioniert genauso einfach, deshalb sparen wir uns das Beispiel an dieser Stelle. Beide Funktionen read() und write() des Treibers verwenden übrigens intern eine sogenannte Semaphore, welche jeweils nur einer Task den Zugriff zu einem Zeitpunkt erlaubt. Dies geschieht, damit mehrere Tasks sich nicht gegenseitig die Daten überschreiben können. Dadurch wird z.B. sichergestellt, dass der read()-Aufruf im obigen Beispiel immer 2 Bytes liefert die zum gleichen Zeitpunkt vom RevPi DIO-Modul eingelesen wurden (Programmierer nennen so etwas „atomarer Zugriff“). Dass zwei Programme abwechselnd auf das gleiche Output-Byte schreiben, kann damit natürlich nicht verhindert werden, dafür bist Du als Anwender verantwortlich. Der Zugriff auf das Prozessabbild benötigt etwa 7 µs bei kleinen Datenmengen von wenigen Bytes. Bei großen Datenmengen von 1 KByte benötigt ein Aufruf 10-13 µs, d.h. es könnten theoretisch 80-100 MByte pro Sekunde in das Prozessabbild geschrieben oder daraus gelesen werden. Der Zugriff über diese Funktionen ist also hoch performant. Wenn Du planst, einen eigenen Treiber z, B. für einen USB Transceiver (Funk oder RS232) zu schreiben, kannst Du diese Funktionsaufrufe verwenden, um zusätzliche Daten ins Prozessabbild einzugliedern.
Damit Du den richtigen Offsetwert für den Zugriff auf Daten (also die korrekte Adresse) verwendest, kannst Du den symbolischen Namen der gewünschten Daten in der JSON Datei suchen und die dazugehörige Position im Speicher sowie die Länge auslesen. Alternativ kannst Du aber auch eine Funktion aufrufen, die von piControl.h bereitgestellt wird und eine Struktur zurückliefert, in der diese Informationen ebenfalls stehen:


int ret;
SPIVariable var;

strcpy(var.strVarName, “Temperatur”);

ret = ioctl(PiControlHandle_g, KB_FIND_VARIABLE, &var);
if (ret < 0)
{
printf(“could not find variable ‘%s’ in configuration.n”, var.strVarName);
}
else
{
printf(“Variable ‘%s’ is a offset %d and %d bits longn”, var.strVarName, var.i16uAddress, var.i8uLength);
}

Wenn Du zu denen gehörst, die es ganz genau wissen wollen, dann schau Dir noch den Beispielcode an, der die Funktionen ioctl() für Setzen von Bit 3 und lesen des Bit 4 des 10. Bytes im Prozessabbild verwendet. Die dafür verwendete Struktur „value“ wird in piControl.h definiert. Der in dieser Struktur verwendete Wert i8uBit darf Werte von 0 bis 7 einnehmen, um das gewünschte Bit zu adressieren. Werte über 7 setzen grundsätzlich alle Bits des adressierten Bytes auf i8uValue:

SPIValue value;

value.i16uAddress = 10; // byte address
value.i8uBit = 3; // bit 3 of byte 10
value.i8uValue = 1; // set to 1

ret = ioctl(file_hd, KB_SET_VALUE, &value);

value.i16uAddress = 10; // byte address
value.i8uBit = 4; // bit 4 of byte 10

ret = ioctl(file_hd, KB_GET_VALUE, &value);

if (value.i8uValue)
printf(“bit 4 is setn”);

// set byte 11 to 166
value.i16uAddress = 11; // byte address
value.i8uBit = 8; // set the whole byte
value.i8uValue = 166;

ret = ioctl(file_hd, KB_SET_VALUE, &value);

Die Sache mit der “Konsistenz”

In der realen Welt gibt es ja streng genommen keine Gleichzeitigkeit, denn die Dinge passieren in der Regel nacheinander, auch wenn der Zeitunterschied manchmal nicht mehr messbar ist. Aber wenn Du für eine Steuerung mehrere Eingänge miteinander verknüpfen willst, so interessieren Dich diese kleinen Zeitunterschiede nicht. Vielmehr schaust Du auf alle Eingänge in einem kleinen Zeitfenster (die Zeit, die Dein System braucht, um die Zustände zu erfassen). Du machst praktisch eine Art Snapshot von den Eingängen. Und mit diesem Snapshot arbeitet die Logik der Steuerung. Wenn die Ergebnisse ermittelt sind, wird der nächste Snapshot angefertigt, usw. In der Regel ist dieser Snapshot in einer Steuerung eben genau das immer wieder erwähnte Prozessabbild. Von diesem fordert man oft, dass es „konsistent“ sein muss. Darunter wird im Allgemeinen verstanden, dass die kurze Zeit der Erfassung aller Eingänge so kurz sein muss, dass nicht bei der Erfassung des letzten Eingangs der erste schon einen anderen Zustand angenommen haben kann. Man möchte also wirklich am liebsten so eine Art Foto, auf dem die Zustände zu einem einzigen Zeitpunkt eingefroren wurden.

Bei unserem modularen System ist so eine durchgängig Konsistenz nicht realisierbar, denn die vielen Datenquellen (fremde Bussysteme über Gateways, IO-Module, USB-Transceiver, Geräte am anderen Ende des Ethernet-Netzwerks, Cloudserver, etc.), welche das Prozessabbild speisen, sind nicht synchronisierbar.

Beim Revolution Pi Prozessabbild gibt es folgende eingebaute Konsistenzen:

  • Alle Aufrufe von write() oder read() auf das Prozessabbild sind für ihre
    jeweiligen Daten konsistent
  • Alle einfachen Eingangswerte eines RevPi DIO (also maximal 16 binäre Inputs)
    sind im Prozessabbild konsistent
  • In einer späteren Version können mehrere RevPi DIO Module über ein Freeze-
    Kommando eingefroren werden und so als konsistente Einheit betrachtet
    werden
  • Alle RevPi Gate Datenblöcke sind konsistent aus einem Feldbus entnommen
    bzw. werden konsistent dorthin gesendet. Ob es sich aber dabei um
    konsistente Daten handelt bestimmt das fremde Bussystem

4 Gedanken zu „Volle Kontrolle mit PiControl“

  1. Hallo,

    ich habe vor, in einer Anwendung mit dem Prozessimage Daten auszutauschen. Mich würde interessieren, ob es möglich ist, Änderungen im Prozessimage asynchron als Event zu erhalten. Wenn die Anwendung an alle Änderungen einer Variable im Image interessiert ist, müsste sie alternativ die Variable pollen, so wie oben in piTest passiert. Hinsichtlich der Performanz stellt das natürlich den worst-case dar. Welche Vorgehensweise würde KUNBUS vorschlagen.

    Grüße,
    Zekeriya

    1. Hallo Zekeriya,
      bitte verwende für technische Fragen unser Forum. Ich hoffe, dass der Forums-Zugriff inzwischen bei Dir funktioniert. Wir können hier im Blog leider aus organisatorischen gründen nicht so zeitnah Fragen beantworten, wie im Forum und dort soll auch
      Zu deiner Frage:
      Die Software wurde von KUNBUS zunächst für EN61131 basierte Steuerungsaufgaben konzipiert. Diese Norm ist aber immer auf zyklische Datenauswertung und nicht auf Event-basierte technologien ausgelegt. Inzwischen haben wir natürlich gemerkt, dass unsere Kunden u.a. durch IoT und andere technologien auch sehr gerne eventbasierte Lösungen stricken wollen. Daher ist ein erweiterter PiControl-treiber bei uns auf dme plan. Wir können aber aktuell nur sehr grob ein Ziel dazu nennen: Bis zur SPS/Drives in Nürnberg 2017 wollen wir eine Treiber version vorstellen, bei der alle Anwendungsprogramme die Möglichkeit haben, von zuvor über PiCtory konfigurierte Events benachrichtigt zu werden. Mit dieser Technik könnten Anwendungen dann zum Beispiel nur bei veränderten Werten aktiv werden.

Kommentare sind geschlossen.