1. Einführung

In diesem Abschnitt wird die Zusammenstellung von HAL-Komponenten vorgestellt, d. h. die Hinzufügung einiger Kenntnisse der Maschinenbediener über den Umgang mit der Maschine. Es ist zu beachten, dass solche Komponenten nicht unbedingt direkt mit der Hardware zu tun haben. Sie tun es oft, aber nicht notwendigerweise, z.B. könnte es eine Komponente geben, die zwischen imperialen und metrischen Maßstäben umrechnet, so dass es in diesem Abschnitt nicht erforderlich ist, auf die Interaktion mit der Hardware einzugehen.

Das Schreiben einer HAL-Komponente kann ein langwieriger Prozess sein, die meisten davon in Setup-Aufrufe zu rtapi_ und hal_ Funktionen und damit verbundene Fehlerprüfung. halcompile wird all diesen Code für Sie schreiben, automatisch. Das Kompilieren einer HAL-Komponente ist auch viel einfacher, wenn man halcompile benutzt, egal ob die Komponente Teil des LinuxCNC-Source-Trees ist, oder außerhalb davon.

Eine einfache Komponente wie "ddt", die in C kodiert ist, umfasst beispielsweise etwa 80 Zeilen Code. Die entsprechende Komponente ist sehr kurz, wenn sie mit dem Präprozessor "halcompile" geschrieben wird:

Beispiel für eine einfache Komponente
component ddt "Berechne die Ableitung der Eingangsfunktion";
pin in float in;
pin out float out;
variable double old;
function _;
license "GPL"; // gibt GPL v2 oder höher an
;;
float tmp = in;
out = (tmp - old) / fperiod;
old = tmp;

2. Installation

Um eine Komponente zu kompilieren, wenn eine gepackte Version von LinuxCNC verwendet wird, müssen Entwicklungspakete installiert werden, indem man entweder Synaptic aus dem Hauptmenü System -> Administration -> Synaptic package manager benutzt oder einen der folgenden Befehle in einem Terminalfenster ausführt:

Installation von Entwicklungspaketen für LinuxCNC
sudo apt install linuxcnc-dev
# oder
sudo apt install linuxcnc-uspace-dev

Eine andere Methode ist die Verwendung des Synaptic-Paketmanagers aus dem Anwendungsmenü, um die Pakete linuxcnc-dev oder linuxcnc-uspace-dev zu installieren.

3. Verwendung einer Komponente

Die Komponenten müssen geladen und zu einem Thread hinzugefügt werden, bevor sie eingesetzt werden können. Die bereitgestellte Funktionalität kann dann direkt und wiederholt von einem der Threads aufgerufen werden oder sie wird von anderen Komponenten aufgerufen, die ihre eigenen Auslöser haben.

Beispiel für ein HAL-Skript zur Installation einer Komponente (ddt), die jede Millisekunde ausgeführt wird.
loadrt threads name1=servo-thread period1=1000000
loadrt ddt
addf ddt.0 servo-thread

More information on loadrt and addf can be found in the HAL Grundlagen.

Um Ihre Komponente zu testen, können Sie den Beispielen im HAL Tutorial folgen.

4. Definitionen

  • component - Eine Komponente ist ein einzelnes Echtzeitmodul, das mit halcmd loadrt geladen wird. Eine .comp-Datei gibt eine Komponente an. Der Komponentenname und der Dateiname müssen übereinstimmen.

  • instance – Eine Komponente kann null oder mehr Instanzen haben. Jede Instanz einer Komponente wird gleich erstellt (sie haben alle dieselben Pins, Parameter, Funktionen und Daten), verhalten sich jedoch unabhängig, wenn ihre Pins, Parameter und Daten unterschiedliche Werte haben.

  • singleton - Es ist möglich, dass eine Komponente ein "Singleton" ist, in diesem Fall wird genau eine Instanz erstellt. Es ist selten sinnvoll, eine "Singleton"-Komponente zu schreiben, es sei denn, es kann buchstäblich nur ein einziges Objekt dieser Art im System geben (z.B. eine Komponente, deren Zweck es ist, einen Pin mit der aktuellen UNIX-Zeit zu versehen, oder ein Hardware-Treiber für den internen PC-Lautsprecher).

5. Erstellung einer Instanz

Bei einem Singleton wird eine Instanz erstellt, wenn die Komponente geladen wird.

Bei einem Nicht-Singleton bestimmt der Modulparameter "count", wie viele nummerierte Instanzen erstellt werden. Wenn count nicht angegeben wird, bestimmt der Modulparameter names, wie viele benannte Instanzen erstellt werden. Wenn weder count noch names angegeben werden, wird eine einzige nummerierte Instanz erstellt.

6. Implizite Parameter

Den Funktionen wird implizit der Parameter period übergeben, der die Zeit in Nanosekunden der letzten Periode zur Ausführung der Komponente angibt. Funktionen, die Fließkommazahlen verwenden, können sich auch auf den Parameter fperiod beziehen, der die Fließkommazeit in Sekunden oder (period*1e-9) angibt. Dies kann in Komponenten nützlich sein, die Zeitinformationen benötigen.

7. Syntax

Eine .comp-Datei besteht aus einer Reihe von Deklarationen, gefolgt von ;; auf einer eigenen Zeile, gefolgt von C Code, der die Funktionen des Moduls implementiert.

Die Erklärungen umfassen:

  • component HALNAME (DOC);

  • pin PINDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE: CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC) ;

  • param PARAMDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE: CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC) ;

  • function HALNAME (fp | nofp) (DOC);

  • option OPT (VALUE);

  • variable CTYPE STARREDNAME ([SIZE]);

  • description DOC;

  • examples DOC;

  • notes DOC;

  • see_also DOC;'

  • license LICENSE;

  • author AUTHOR;

  • include HEADERFILE;

Klammern kennzeichnen optionale Elemente. Ein senkrechter Strich kennzeichnet Alternativen. Wörter in "GROSSBUCHSTABEN" kennzeichnen variablen Text, wie folgt:

  • NAME - Ein Standard-C-Bezeichner

  • STARREDNAME' - Ein C-Bezeichner mit null oder mehr * vor dem Namen. Diese Syntax kann verwendet werden, um Instanzvariablen zu deklarieren, die Zeiger sind. Beachten Sie, dass aufgrund der Grammatik kein Leerzeichen zwischen dem * und dem Variablennamen stehen darf.

  • HALNAME - Ein erweiterter Bezeichner. Bei der Erstellung eines HAL-Bezeichners werden alle Unterstriche durch Bindestriche ersetzt, und alle nachgestellten Bindestriche oder Punkte werden entfernt, so dass "this_name_" in "dieser-Name" umgewandelt wird, und wenn der Name "_" ist, wird auch ein nachgestellter Punkt entfernt, so dass "function _" einen HAL-Funktionsnamen wie "component" ergibt. " <num>statt "Komponente. <num>."

    Falls vorhanden, wird beim Erstellen von Pins, Parametern und Funktionen das Präfix hal_ am Anfang des Komponentennamens entfernt.

Im HAL-Bezeichner für einen Pin oder Parameter kennzeichnet # ein Arrayelement und muss in Verbindung mit einer [SIZE]-Deklaration verwendet werden. Die Rautenzeichen werden durch eine 0-aufgefüllte Zahl ersetzt mit der gleichen Länge wie die Anzahl der #-Zeichen.

Wenn Sie einen C-Bezeichner erstellen, werden die folgenden Änderungen am HALNAME vorgenommen:

  1. Alle "#"-Zeichen und alle Zeichen ".", "_" oder "-", die unmittelbar davor stehen, werden entfernt.

  2. Alle verbleibenden "."- und "-"-Zeichen werden durch "_" ersetzt.

  3. Wiederholte „\_“-Zeichen werden in ein einzelnes „\_“-Zeichen geändert.

Ein nachgestelltes "_" wird beibehalten, damit HAL-Kennungen, die sonst mit reservierten Namen oder Schlüsselwörtern (z. B. "min") kollidieren würden, verwendet werden können.

HALNAME C Bezeichner (engl. identifier) HAL-Bezeichner (engl. identifier)

x_y_z

x_y_z

x-y-z

x-y.z

x_y_z

x-y.z

x_y_z_

x_y_z_

x-y-z

x.##.y

x_y(MM)

x.MM.z

x.##

x(MM)

x.MM

  • if CONDITION (engl. für Bedingung)- Ein Ausdruck mit der Variablen Persönlichkeit, die ungleich Null ist, wenn der Pin oder Parameter erstellt werden soll.

  • SIZE - Eine Zahl, um die Größe eines Arrays anzugeben. Die Array-Elemente sind von 0 bis SIZE-1 nummeriert.

  • MAXSIZE : CONDSIZE - Gibt die maximale Größe des Arrays an, gefolgt von einem Ausdruck, der die Variable personality einbezieht und der immer weniger als MAXSIZE ergibt. Wenn das Array erstellt wird, hat es die Größe CONDSIZE.

  • DOC - Eine Zeichenfolge, die das Element dokumentiert. Die Zeichenfolge kann eine "doppelt in Anführungszeichen" gesetzte Zeichenfolge im C-Stil sein, z. B.:

    "Wählt die gewünschte Flanke aus: TRUE bedeutet fallend, FALSE bedeutet steigend"

    oder eine "dreifach in Anführungszeichen" gesetzte Zeichenfolge im Python-Stil, die eingebettete Zeilenumbrüche und Anführungszeichen enthalten kann, z. B.:

    """Die Wirkung dieses Parameters, auch bekannt als "der Orb von Zot",
    ist in mindestens zwei Absätzen zu erklären.
    
    Hoffentlich haben Ihnen diese Absätze geholfen, "zot" besser
    zu verstehen."""

    Einer Zeichenkette kann auch das Literalzeichen r vorangestellt werden; in diesem Fall wird die Zeichenkette wie eine Python-Rohzeichenkette interpretiert.

    Die Dokumentationszeichenfolge hat das Format "groff -man". Für weitere Informationen über dieses Format siehe groff_man(7). Denken Sie daran, dass halcompile Backslash-Escapes in Zeichenketten interpretiert, so dass Sie zum Beispiel die kursive Schriftart für das Wort Beispiel einstellen können:

    "\\fIBeispiel\\fB"

    In diesem Fall sind r-Zeichenfolgen besonders nützlich, da die Backslashes in einer r-Zeichenfolge nicht verdoppelt werden müssen:

    r"\fI-Beispiel\fB"
  • TYPE - Einer der HAL-Typen: bit, s32, u32, s64, u64 or float. Die Namen signed (engl. für "mit Vorzeichen") und unsigned können ebenfalls verwendet werden statt s32 oder u32, aber letzteres wird lieber gesehen.

  • PINDIRECTION - Eine der folgenden Optionen: in, out oder io. Eine Komponente legt einen Wert für einen Out-Pin fest, liest einen Wert von einem "In"-Pin und kann den Wert eines "io"-Pins lesen oder festlegen.

  • PARAMDIRECTION - Eine der folgenden: r oder rw. Eine Komponente legt einen Wert für einen r-Parameter fest und kann den Wert eines rw-Parameters lesen oder festlegen.

  • STARTVALUE - Gibt den Anfangswert eines Pins oder Parameters an. Wenn nicht anders angegeben, ist der Standardwert "0" oder "FALSE", abhängig vom Typ des Elements.

  • HEADERFILE - Der Name einer Headerdatei, entweder in doppelten Anführungszeichen (include "myfile.h";) oder in spitzen Klammern (include <systemfile.h>;). Die Header-Datei wird (unter Verwendung der #include von C) am Anfang der Datei vor Pin- und Parameterdeklarationen eingefügt.

7.1. HAL-Funktionen

  • fp - Gibt an, dass die Funktion Gleitkommaberechnungen durchführt.

  • nofp - Gibt an, dass nur Ganzzahlberechnungen durchgeführt werden. Wenn keines von beiden angegeben ist, wird fp angenommen. Weder halcompile noch gcc können die Verwendung von Fließkommaberechnungen in Funktionen, die mit nofp gekennzeichnet sind, erkennen, aber die Verwendung solcher Operationen führt zu undefiniertem Verhalten.

7.2. Optionen

Die derzeit definierten Optionen sind:

  • option singleton yes - (Standardwert: no) Erzeugt keinen count-Modulparameter und immer eine einzelne Instanz. Mit singleton werden die Elemente Komponentenname.Elementname genannt und ohne singleton werden die Elemente für nummerierte Instanzen Komponentenname.<num>.Elementname genannt.

  • option default_count number' - (Standardwert: 1) Normalerweise ist der Modulparameter count auf 1 voreingestellt. Ist er angegeben, so wird count stattdessen auf diesen Wert gesetzt.

  • option count_function yes - (Voreinstellung: no) Normalerweise wird die Anzahl der zu erstellenden Instanzen im Modulparameter count angegeben; wenn count_function angegeben ist, wird stattdessen der von der Funktion int get_count(void) zurückgegebene Wert verwendet, und der Modulparameter count ist nicht definiert.

  • option rtapi_app no - (Voreinstellung: yes) Normalerweise werden die Funktionen rtapi_app_main() und rtapi_app_exit() automatisch definiert. Bei option rtapi_app no sind sie es nicht und müssen im C-Code bereitgestellt werden. Verwenden Sie die folgenden Prototypen:

    `int rtapi_app_main(void);`
    
    `void rtapi_app_exit(void);`

    Wenn Sie Ihre eigene rtapi_app_main() implementieren, rufen Sie die Funktion int export(char *prefix, long extra_arg) auf, um die Pins, Parameter und Funktionen für prefix zu registrieren.

  • option data TYPE - (Voreinstellung: none) veraltet
    Wenn angegeben, hat jede Instanz der Komponente einen zugehörigen Datenblock des Typs TYPE (der ein einfacher Typ wie float oder der Name eines mit typedef erstellten Typs sein kann). In neuen Komponenten sollte stattdessen variable verwendet werden.

  • option extra_setup yes - (Standard: no)
    Wenn angegeben, wird die durch EXTRA_SETUP definierte Funktion für jede Instanz aufgerufen. Bei Verwendung der automatisch definierten rtapi_app_main ist extra_arg die Nummer dieser Instanz.

  • option extra_cleanup yes' - (Voreinstellung: no)
    Wenn angegeben, wird die durch EXTRA_CLEANUP definierte Funktion aus dem automatisch definierten rtapi_app_exit oder, im Falle eines erkannten Fehlers, im automatisch definierten rtapi_app_main aufgerufen.

  • option userspace yes - (Voreinstellung: no)
    Falls angegeben, beschreibt diese Datei eine Nicht-Echtzeit-Komponente (früher bekannt als "Userspace") und nicht eine reguläre (d.h. Echtzeit-) Komponente. Eine Nicht-Echtzeit-Komponente kann keine Funktionen haben, die durch die function-Direktive definiert sind. Stattdessen wird, nachdem alle Instanzen konstruiert sind, die C-Funktion void user_mainloop(void); aufgerufen. Wenn diese Funktion zurückkehrt, wird die Komponente beendet. Normalerweise verwendet user_mainloop() FOR_ALL_INSTS(), um die Aktualisierungsaktion für jede Instanz durchzuführen, und schläft dann für eine kurze Zeit. Eine andere übliche Aktion in user_mainloop() kann der Aufruf der Event-Handler-Schleife eines GUI-Toolkits sein.

  • option userinit yes - (Standard: no)
    Diese Option wird ignoriert, wenn die Option userspace (siehe oben) auf no gesetzt ist. Wenn userinit angegeben ist, wird die Funktion userinit(argc,argv) vor rtapi_app_main() (und damit vor dem Aufruf von hal_init() ) aufgerufen. Diese Funktion kann die Kommandozeilenargumente verarbeiten oder andere Aktionen ausführen. Ihr Rückgabetyp ist void; sie kann exit() aufrufen, wenn sie beenden will, anstatt eine HAL-Komponente zu erstellen (z.B. weil die Kommandozeilenargumente ungültig waren).

  • option extra_link_args "…" - (default: "") Diese Option wird ignoriert, wenn die Option Userspace (siehe oben) auf no gesetzt ist. Beim Linken einer Nicht-Echtzeitkomponente werden die angegebenen Argumente in die Linkzeile eingefügt. Da die Kompilierung in einem temporären Verzeichnis stattfindet, bezieht sich "-L." auf das temporäre Verzeichnis und nicht auf das Verzeichnis, in dem sich die .comp-Quelldatei befindet. Diese Option kann in der halcompile Befehlszeile mit -extra-link-args="-L….." gesetzt werden. Diese Alternative bietet eine Möglichkeit, zusätzliche Flags in Fällen zu setzen, in denen die Eingabedatei eine .c-Datei und keine .comp-Datei ist.

  • option extra_compile_args "…" - (default: "") Diese Option wird ignoriert, wenn die Option userspace (siehe oben) auf no gesetzt ist. Beim Kompilieren einer Nicht-Echtzeit-Komponente werden die angegebenen Argumente in die Compiler-Befehlszeile eingefügt. Wenn die Eingabedatei eine .c-Datei ist, kann diese Option in der halcompile-Befehlszeile mit --extra-compile-args="-I….." gesetzt werden. Diese Alternative bietet eine Möglichkeit, zusätzliche Flags zu setzen, wenn die Eingabedatei eine .c-Datei und keine .comp-Datei ist.

  • option homemod yes - (Standard: no)
    Modul ist ein benutzerdefiniertes Homing-Modul, das mit [EMCMOT]HOMEMOD=Modulname geladen wird.

  • option tpmod yes - (Standard: no)
    Modul ist ein benutzerdefiniertes Trajektorienplanungsmodul (tp), das mit [TRAJ]TPMOD=_Modulname geladen wird.

Wenn der VALUE (engl. für Wert) einer Option nicht angegeben wird, ist dies gleichbedeutend mit der Angabe von option … yes.
Das Ergebnis der Zuweisung eines unangemessenen Wertes zu einer Option ist undefiniert
Das Ergebnis der Verwendung einer anderen Option ist undefiniert.

7.3. Lizenz und Urheberschaft

  • LICENSE - Geben Sie die Lizenz des Moduls für die Dokumentation und für die MODULE_LICENSE()-Moduldeklaration an. Zum Beispiel, um anzugeben, dass die Lizenz des Moduls GPL v2 oder höher ist:

    `license "GPL"; // bedeutet GPL v2 oder höher`

    Weitere Informationen über die Bedeutung von MODULE_LICENSE() und zusätzliche Lizenzbezeichner finden Sie in <linux/module.h> oder in der Handbuchseite zu rtapi_module_param(3).

    Diese Erklärung ist erforderlich.

  • AUTHOR - Geben Sie den Autor des Moduls für die Dokumentation an.

7.4. Datenspeicherung pro Instanz

  • variable CTYPE STARREDNAME; + variable CTYPE STARREDNAME[SIZE]; + variable CTYPE STARREDNAME = DEFAULT; + variable CTYPE STARREDNAME[SIZE] = DEFAULT;

    Deklarieren Sie eine Instanzvariable STARREDNAME vom Typ CTYPE, optional als Array von SIZE-Elementen und optional mit einem Standardwert DEFAULT. Elemente ohne DEFAULT werden auf alle Bits-Null initialisiert. CTYPE ist ein einfacher Ein-Wort-C-Typ, wie float, u32, s32, int, etc. Der Zugriff auf Array-Variablen erfolgt über eckige Klammern.

Wenn eine Variable ein Zeigertyp sein soll, darf zwischen dem "*" und dem Variablennamen kein Leerzeichen stehen. Daher ist das Folgende akzeptabel:

variable int *example;

Aber die folgenden sind es nicht:

variable int* badexample;
variable int * badexample;

7.5. Kommentare

Einzeilige Kommentare im C++-Stil (//...) und mehrzeilige Kommentare im C-Stil (/* ... */) werden beide im Deklarationsabschnitt unterstützt.

8. Einschränkungen

Obwohl HAL erlaubt, dass ein Pin, ein Parameter und eine Funktion denselben Namen haben können, ist dies bei halcompile nicht der Fall.

Zu den Variablen- und Funktionsnamen, die nicht verwendet werden können oder zu Problemen führen können, gehören:

  • Alles, was mit _comp beginnt.

  • comp_id

  • fperiod

  • rtapi_app_main

  • rtapi_app_exit

  • extra_setup

  • extra_cleanup

9. Bequemlichkeits-Makros

Basierend auf den Elementen im Deklarationsabschnitt erzeugt halcompile eine C-Struktur namens struct __comp_state. Anstatt jedoch auf die Mitglieder dieser Struktur zu verweisen (z.B. *(inst->name)), werden sie im Allgemeinen mit den untenstehenden Makros angesprochen. Die Details von struct __comp_state und diesen Makros können sich von einer Version von halcompile zur nächsten ändern.

  • FUNCTION(`__name__)` - Verwenden Sie dieses Makro, um die Definition einer Echtzeitfunktion zu beginnen, die zuvor mit function NAME deklariert wurde. Die Funktion enthält einen Parameter period, der die ganzzahlige Anzahl von Nanosekunden zwischen Aufrufen der Funktion angibt.

  • EXTRA_SETUP() - Verwenden Sie dieses Makro, um die Definition der Funktion zu beginnen, die aufgerufen wird, um eine zusätzliche Einrichtung dieser Instanz durchzuführen. Geben Sie einen negativen Unix-errno-Wert zurück, um einen Fehler anzuzeigen (z.B. return -EBUSY, wenn die Reservierung eines I/O-Ports fehlgeschlagen ist), oder 0, um einen Erfolg anzuzeigen.

  • EXTRA_CLEANUP() - Verwenden Sie dieses Makro zu Beginn der Definition derjenigen Funktion, die eine Erweiterung des Aufräumen der Komponente implementiert. Beachten Sie, dass diese Funktion alle Instanzen der Komponente aufräumen muss, nicht nur eine. Die Makros "pin_name", "parameter_name" und "data" dürfen hier nicht verwendet werden.

  • pin_name oder parameter_name - Für jeden Pin pin_name oder Parameter parameter_name gibt es ein Makro, mit dem der Name allein verwendet werden kann, um auf den Pin oder Parameter zu verweisen. Wenn pin_name oder parameter_name ein Array ist, hat das Makro die Form pin_name(idx) oder param_name(idx), wobei idx der Index im Pin-Array ist. Handelt es sich bei dem Array um ein Array mit variabler Größe, ist es nur zulässig, um auf Elemente bis zu seiner condsize zu verweisen.

    Wenn es sich um eine bedingte Position handelt, kann nur auf sie verwiesen werden, wenn ihre "Bedingung" einen Wert ungleich Null ergibt.

  • variable_name - Für jede Variable variable_name gibt es ein Makro, das es erlaubt, den Namen allein zu verwenden, um auf die Variable zu verweisen. Wenn variable_name ein Array ist, wird das normale C-Subskript verwendet: variable_name[idx].

  • data - Wenn "option data" angegeben ist, ermöglicht dieses Makro den Zugriff auf die Instanzdaten.

  • fperiod - Die Gleitkommazahl von Sekunden zwischen Aufrufen dieser Echtzeitfunktion.

  • FOR_ALL_INSTS() `{} - Für Nicht-Echtzeit-Komponenten. Dieses Makro iteriert über alle definierten Instanzen. Innerhalb des Schleifenkörpers arbeiten die Makros pin_name, parameter_name und data wie in Echtzeitfunktionen.

10. Komponenten mit einer Funktion

Wenn eine Komponente nur eine Funktion hat und die Zeichenkette "FUNCTION" nirgendwo nach ;; auftaucht, dann wird der Teil nach ;; als der Körper der einzigen Funktion der Komponente angesehen. Siehe Simple Comp für ein Beispiel hierfür.

11. Komponenten-Persönlichkeit

Wenn eine Komponente Pins oder Parameter mit einer "if-Bedingung" oder "[maxsize : condsize]" hat, wird sie als Komponente mit "Persönlichkeit" bezeichnet. Die "Persönlichkeit" jeder Instanz wird beim Laden des Moduls festgelegt. Die "Persönlichkeit" kann verwendet werden, um Pins nur bei Bedarf zu erstellen. So wird die "Persönlichkeit" beispielsweise in der Komponente logic (engl. für Logik) verwendet, um eine variable Anzahl von Eingangspins für jedes Logikgatter und die Auswahl einer der grundlegenden booleschen Logikfunktionen und, oder und xor zu ermöglichen.

Die Standardanzahl der erlaubten "personality"-Elemente ist eine Kompilierzeiteinstellung (64). Die Vorgabe gilt für zahlreiche in der Distribution enthaltene Komponenten, die mit halcompile erstellt werden.

Um die zulässige Anzahl von Persönlichkeitselementen für benutzerdefinierte Komponenten zu ändern, verwenden Sie die Option --personalities mit halcompile. Zum Beispiel, um bis zu 128 Persönlichkeitszeiten zu erlauben:

  [sudo] halcompile --personalities=128 --install ...

Bei der Verwendung von Komponenten mit Persönlichkeit ist es üblich, ein Persönlichkeitselement für jede angegebene Komponenteninstanz anzugeben. Beispiel für 3 Instanzen der Logikkomponente:

loadrt logic names=and4,or3,nand5, personality=0x104,0x203,0x805
Anmerkung
Wenn eine loadrt-Zeile mehr Instanzen als Persönlichkeiten angibt, wird den Instanzen mit nicht angegebenen Persönlichkeiten eine Persönlichkeit von 0 zugewiesen. Wenn die angeforderte Anzahl von Instanzen die Anzahl der erlaubten Persönlichkeiten übersteigt, werden die Persönlichkeiten durch Indexierung modulo der Anzahl der erlaubten Persönlichkeiten zugewiesen. Es wird eine Meldung über solche Zuweisungen ausgegeben.

12. Kompilieren

Legen Sie die .comp-Datei in das Quellverzeichnis linuxcnc/src/hal/components und führen Sie make erneut aus. Comp-Dateien werden vom Build-System automatisch erkannt.

Wenn eine .comp-Datei ein Treiber für Hardware ist, kann sie in linuxcnc/src/hal/drivers abgelegt werden und wird gebaut, es sei denn, LinuxCNC ist als Nicht-Echtzeit-Simulator konfiguriert.

13. Kompilieren von Echtzeitkomponenten außerhalb des Quellbaums

halcompile kann eine Echtzeitkomponente in einem einzigen Schritt verarbeiten, kompilieren und installieren, wobei rtexample.ko im LinuxCNC-Echtzeitmodulverzeichnis platziert wird:

[sudo] halcompile --install rtexample.comp
Anmerkung
sudo (für Root-Rechte) wird benötigt, wenn Sie LinuxCNC aus einem Deb-Paket installieren. Wenn Sie einen Run-In-Place (RIP) Build verwenden, sollten Root-Rechte nicht erforderlich sein.

Oder es kann in einem Schritt verarbeitet und kompiliert werden, wobei example.ko (oder example.so für den Simulator) im aktuellen Verzeichnis verbleibt:

halcompile --compile rtexample.comp

Oder es kann einfach verarbeitet werden, wobei die Datei "example.c" im aktuellen Verzeichnis verbleibt:

halcompile rtexample.comp

halcompile kann auch eine in C geschriebene Komponente kompilieren und installieren, indem es die oben gezeigten Optionen --install und --compile verwendet:

[sudo] halcompile --install rtexample2.c

Die Dokumentation im man-Format kann auch aus den Informationen im Deklarationsabschnitt erstellt werden:

halcompile --document rtexample.comp

Die resultierende Manpage „example.9“ kann angezeigt werden mit

man ./example.9

oder an einen Standardspeicherort für UNIX man pages kopiert.

14. Kompilieren von Nicht-Echtzeit-komponenten außerhalb des Quellbaums

halcompile kann Nicht-Echtzeit-Komponenten verarbeiten, kompilieren, installieren und dokumentieren:

halcompile non-rt-example.comp
halcompile --compile non-rt-example.comp
[sudo] halcompile --install non-rt-example.comp
halcompile --document non-rt-example.comp

Bei einigen Bibliotheken (z. B. Modbus) kann es erforderlich sein, zusätzliche Compiler- und Linker-Argumente hinzuzufügen, damit der Compiler die Bibliotheken finden und linken kann. Im Falle von .comp-Dateien kann dies über "option"-Anweisungen in der .comp-Datei erfolgen. Für .c-Dateien ist dies nicht möglich, so dass stattdessen die Parameter --extra-compile-args und --extra-link-args verwendet werden können. Als Beispiel kann diese Befehlszeile verwendet werden, um die Komponente vfdb_vfd.c out-of-tree zu kompilieren.

halcompile --userspace --install --extra-compile-args="-I/usr/include/modbus" --extra-link-args="-lm -lmodbus -llinuxcncini" vfdb_vfd.c
Anmerkung
Die Auswirkung der Verwendung von extra-args in der Befehlszeile und in der Datei ist undefiniert.

15. Beispiele

15.1. Konstante

Beachten Sie, dass die Deklaration "function _" Funktionen mit dem Namen "constant.0" usw. erzeugt. Der Dateiname muss mit dem Komponentennamen übereinstimmen.

component constant;
pin out float out;
param r float value = 1.0;
function _;
license "GPL"; // bedeutet GPL v2 oder höher
;;
FUNCTION(_) { out = value; }

15.2. sincos

Diese Komponente berechnet den Sinus und Kosinus eines Eingangswinkels im Bogenmaß. Sie hat andere Fähigkeiten als die "Sinus"- und "Kosinus"-Ausgänge von siggen, weil die Eingabe ein Winkel ist und nicht frei auf der Grundlage eines "Frequenz"-Parameters läuft.

Die Pins werden im Quellcode mit den Namen sin_ und cos_ deklariert, damit sie nicht mit den Funktionen sin() und cos() interferieren. Die HAL-Pins heißen weiterhin sincos.<num>.sin.

component sincos;
pin out float sin_;
pin out float cos_;
pin in float theta;
function _;
license "GPL"; // bedeutet GPL v2 oder höher
;;
#include <rtapi_math.h>
FUNCTION(_) { sin_ = sin(theta); cos_ = cos(theta); }

15.3. out8

Bei dieser Komponente handelt es sich um einen Treiber für eine "fiktive" Karte mit der Bezeichnung "out8", die über 8 Pins mit digitalen Ausgängen verfügt, die als ein einziger 8-Bit-Wert behandelt werden. Es kann eine unterschiedliche Anzahl solcher Karten im System geben, und sie können sich an verschiedenen Adressen befinden. Der Pin wird out_ genannt, weil out ein in <asm/io.h> verwendeter Bezeichner ist. Er veranschaulicht die Verwendung von EXTRA_SETUP und EXTRA_CLEANUP, um einen E/A-Bereich anzufordern und ihn dann im Fehlerfall oder beim Entladen des Moduls wieder freizugeben.

component out8;
pin out unsigned out_ "Ausgabewert; es werden nur niedrige 8 Bit verwendet";
param r unsigned ioaddr;

function _;

option count_function;
option extra_setup;
option extra_cleanup;
option constructable no;

license "GPL"; // bedeutet GPL v2 oder höher
;;
#include <asm/io.h>

#define MAX 8
int io[MAX] = {0,};
RTAPI_MP_ARRAY_INT(io, MAX, "E/A-Adressen der out8-Karten");

int get_count(void) {
    int i = 0;
    for(i=0; i<MAX && io[i]; i++) { /* Nichts */ }
    return i;
}

EXTRA_SETUP() {
    if(!rtapi_request_region(io[extra_arg], 1, "out8")) {
        // Setze diesen I/O-Port auf 0, damit EXTRA_CLEANUP die IO-Ports nicht freigibt,
        // die nie angefordert wurden.
        io[extra_arg] = 0;
        return -EBUSY;
    }
    ioaddr = io[extra_arg];
    return 0;
}

EXTRA_CLEANUP() {
    int i;
    for(i=0; i < MAX && io[i]; i++) {
        rtapi_release_region(io[i], 1);
    }
}

FUNCTION(_) { outb(out_, ioaddr); }

15.4. hal_loop

component hal_loop;
pin out float example;

Dieses Fragment einer Komponente veranschaulicht die Verwendung des Präfixes "hal_" in einem Komponentennamen.

loop ist ein gebräuchlicher Name (in der englischsprachig dominierten Programmierung), und das Präfix hal_ vermeidet mögliche Namenskollisionen mit anderer, nicht verwandter Software. Zum Beispiel läuft auf RTAI-Echtzeitsystemen Echtzeitcode im Kernel, wenn die Komponente also nur "loop" heißen würde, könnte sie leicht mit dem Standard-Kernelmodul "loop" in Konflikt geraten.

Nach dem Laden zeigt halcmd show comp eine Komponente namens hal_loop an. Der von "halcmd show pin" angezeigte Pin ist jedoch "loop.0.example" und nicht "hal-loop.0.example".

15.5. arraydemo

Diese Echtzeitkomponente veranschaulicht die Verwendung von Arrays fester Größe:

component arraydemo "4-Bit-Schieberegister";
pin in bit in;
pin out bit out-# [4];
funktion _ nofp;
licencse "GPL"; // bedeutet GPL v2 oder höher
;;
int i;
for(i=3; i>0; i--) out(i) = out(i-1);
out(0) = in;

15.6. rand

Diese Nicht-Echtzeit-Komponente ändert den Wert an ihrem Ausgangspin etwa alle 1 ms auf einen neuen Zufallswert im Bereich (0,1).

component rand;
option userspace;

pin out float out;
license "GPL"; // bedeutet GPL v2 oder höher
;;
#include <unistd.h>

void user_mainloop(void) {
    while(1) {
        usleep(1000);
        FOR_ALL_INSTS() out = drand48();
    }
}

15.7. logic

Diese Echtzeitkomponente zeigt, wie man "Persönlichkeit" verwendet, um Arrays variabler Größe und optionale Pins zu erstellen.

component logic "LinuxCNC HAL Komponente mit experimentellen Logikfunktionen";
pin in bit in-##[16 : personality & 0xff];
pin out bit and if personality & 0x100;
pin out bit or if personality & 0x200;
pin out bit xor if personality & 0x400;
function _ nofp;
description """
Experimentelle allgemeine 'Logikfunktion' Komponente. Kann 'und', 'oder' und 'xor' von bis zu 16 Eingängen durchführen. Bestimmen Sie den richtigen Wert für 'Persönlichkeit' durch Hinzufügen:
.IP \\(bu 4 Die Anzahl der Eingangsstifte, in der Regel von 2 bis 16
.IP \\(bu 256 (0x100), wenn der 'und'-Ausgang gewünscht ist
.IP \\(bu 512 (0x200), wenn der 'oder'-Ausgang erwünscht ist
IP \\(bu 1024 (0x400), wenn die 'xor'-Ausgabe (exklusives oder) gewünscht ist""";
license "GPL"; // bedeutet GPL v2 or höher
;;
FUNCTION(_) {
    int i, a=1, o=0, x=0;
    for(i=0; i < (personality & 0xff); i++) {
        if(in(i)) { o = 1; x = !x; }
        else { a = 0; }
    }
    if(personality & 0x100) and = a;
    if(personality & 0x200) or = o;
    if(personality & 0x400) xor = x;
}

Eine typische Zeile zur Belegung dieses Bauteil könnte lauten

loadrt logic count=3 personality=0x102,0x305,0x503

wodurch die folgenden Pins erstellt werden:

  • A 2-input AND gate: logic.0.and, logic.0.in-00, logic.0.in-01

  • 5-input AND and OR gates: logic.1.and, logic.1.or, logic.1.in-00, logic.1.in-01, logic.1.in-02, logic.1.in-03, logic.1.in-04,

  • 3-input AND and XOR gates: logic.2.and, logic.2.xor, logic.2.in-00, logic.2.in-01, logic.2.in-02

15.8. Allgemeine Funktionen

Dieses Beispiel zeigt, wie man Funktionen von der Hauptfunktion aus aufruft. Es zeigt auch, wie die Referenz von HAL-Pins an diese Funktionen übergeben werden kann.

component example;
pin in s32 in;
pin out bit out1;
pin out bit out2;

function _;
license "GPL";
;;

// allgemeine Pin Set True Funktion
void set(hal_bit_t *p){
    *p = 1;
}

// allgemeine Pin Set False Funktion
void unset(hal_bit_t *p){
    *p = 0;
}

//Haupt-Funktion (engl. main)
FUNCTION(_) {
    if (in < 0){
        set(&out1);
        unset(&out2);
    }else if (in >0){
        unset(&out2);
        set(&out2);
    }else{
        unset(&out1);
        unset(&out2);
    }
}

Diese Komponente verwendet zwei allgemeine Funktionen, um einen HAL-Bit-Pin zu manipulieren, auf den sie referenziert ist.

16. Verwendung der Kommandozeile

Die Manpage zu halcompile enthält Details zum Aufruf von halcompile.

$ man halcompile

Eine kurze Zusammenfassung der Verwendung von halcompile finden Sie hier:

$ halcompile --help