Der Linux Hardware Abstraction Layer (HAL) wird mit den C/C++ Bindings für Mikrocontroller verwendet um mit Bricklets über SPI zu kommunizieren.
Dieser HAL wurde mit dem Raspberry Pi bzw. Pi Zero mit dem HAT Brick bzw. HAT Zero Brick getestet, sollte aber mit allen Linux-basierten Systemen mit spidev-Unterstützung funktionieren. Ein performanterer HAL speziell für den Raspberry Pi ist hier verfügbar.
Bemerkung
Der Raspberry Pi 3, 4 und Zero skalieren den Takt des BCM2835 dynamisch. Leider skaliert dies auch die SPI-Taktfrequenz. Die GPU-Kern-Frequenz muss auf einen festen Wert gesetzt werden, damit die SPI-Taktfrequenz stabil ist. Wir empfehlen, die core_freq in /boot/config.txt auf 250 zu setzen. Siehe hier für Details, inbesondere für den Pi 4B, bei dem die core_freq durch andere Boot-Optionen beeinflusst wird.
Wenn eine core_freq ungleich 250 gewählt wird, muss in hal_raspberry_pi.c
die BRICKLET_STACK_SPI_CONFIG_MAX_SPEED_HZ
-Konstante durch Multiplizieren von
250 / [gewählte core_freq in MHz] kompensiert werden.
Bemerkung
Ein Pegelwandler ist für Geräte, die einen Logik-Pegel von 5V verwenden notwendig.
Dieser HAL beinhaltet einen Beispiel-Treiber, mit dem alle Beispiele, die den Bindings beigelegt sind, ausgeführt werden können, sowie ein Makefile mit dem diese kompiliert werden können. Damit da Makefile verwendet werden kann muss folgende Orderstruktur erstellt werden:
Nachdem die Orderstruktur abgelegt wurde, muss das Makefile für das Beispiel abgeändert werden:
Unter SOURCES_DEVICES
muss das Gerät, das verwendet werden soll aufgeführt werden,
zum Beispiel für das Industrial Digital In 4 Bricklet 2.0:
SOURCES_DEVICES := src/bindings/bricklet_industrial_digital_in_4_v2.c
Der Quellcode des Beispiels selbst muss zu den SOURCES_EXAMPLE
hinzugefügt werden,
zum Beispiel:
SOURCES_EXAMPLE := example_edge_count.c
Als nächstes muss die Port-Zuweisung im Beispiel-Treiber auf den Aufbau angepasst werden (siehe dieser Abschnitt). Wenn mehrere Bricklets am selben SPI-Bus verbunden werden sollen (das ist nur mit einem Trenner-Chip möglich), müssen alle Chip-Select-Pins mit dem Pi verbunden und in der Port-Zuweisung aufgeführt werden, selbst wenn noch keine Kommunikation mit den Bricklets gewünscht ist. Das stellt sicher, dass die Signale korrekt getrennt werden.
Der HAT Brick und HAT Zero Brick beinhalten bereits den benötigten Trenner-Chip und die korrekte Port-Zuweisung ist im Beispiel-Treiber aufgeführt.
Möglicherweise muss nun im Beispiel-Treiber der Pfad zum spidev geändert werden. Der Standardwert
"/dev/spidev0.0"
ist für den :HAT Brick und den
HAT Zero Brick korrekt.
Das Programm kann jetzt mit make
kompiliert werden. Wenn von einem anderen
Rechner aus cross-kompiliert werden soll, kann make CROSS_COMPILE=[compiler-prefix]
verwendet werden, zum Beispiel make CROSS_COMPILE=arm-linux-gnueabihf-
für den Raspberry Pi.
Das Programm kann mit ./uc_example
gestartet werden. Es darf dabei kein Brick Daemon
auf dem Gerät laufen.
Ein Port wird als Instanz der TF_Port
-Struktur spezifiziert:
struct TF_Port {
int chip_select_pin;
char port_name;
}
Relevante Member sind int chip_select_pin
und char port_name
.
Der chip_select_pin
ist der Pin, der gesetzt werden muss, um mit dem Port zu kommunizieren.
Der port_name
ist ein Zeichen, das den Port identifiziert. Der Name wird in die
Ergebnisse von tf_[device]_get_identity
aufrufen eingefügt, falls das Gerät direkt mit dem Host
verbunden ist.
Im Beispiel-Treiber example_driver.c
sind Port-Spezifikationen für den
HAT Brick und HAT Zero Brick enthalten.
Die meisten HAL-Funktionen geben einen Fehlercode (e_code
) zurück.
Mögliche Fehlercodes sind (wie in errors.h
definiert):
Der HAL definiert die folgenden weiteren Fehlercodes:
Mit tf_hal_strerror()
kann eine Fehlerbeschreibung zu einem Fehlercode abgefragt werden.
tf_hal_create
(TF_HAL *hal, const char *spidev_path, TF_Port *ports, uint8_t port_count)¶Erstellt ein HAL-Objekt, das verwendet werden kann um die verfügbaren Geräte aufzulisten. Es wird außerdem für den Konstruktor von Bricks und Bricklets benötigt.
spidev_path
ist der Pfad zum spidev, das verwendet werden soll, zum Beispiel "/dev/spidev0.0".ports
ist ein Array von Port-Spezifikationen, wie hier beschrieben.port_count
ist die Länge des ports
-Array.
tf_hal_destroy
(TF_HAL *hal)¶Zerstört den übergebenen TF_HAL
.
tf_hal_set_timeout
(TF_HAL *hal, uint32_t timeout_us)¶Setzt den Timeout in Mikrosekunden für Getter und Setter für die das Response-Expected-Flag gesetzt ist.
Der Standard-Timeout ist 2500000 (2,5 Sekunden).
tf_hal_get_timeout
(TF_HAL *hal)¶Gibt den Timeout zurück, der von tf_hal_set_timeout()
gesetzt wurde.
tf_hal_get_device_info
(TF_HAL *hal, uint16_t index, char ret_uid[7], char *ret_port_name, uint16_t *ret_device_id)¶Gibt die UID, den Port und den Device-Identifier für das n-te (=index)
gefundene Gerät zurück. Diese Funktion gibt TF_E_DEVICE_NOT_FOUND
zurück, wenn der index größer
oder gleich der Anzahl gefundener Geräte ist. Damit alle Geräte aufgelistet werden, kann diese Funktion
in einer Schleife mit wachsendem index aufgerufen werden, bis einmal TF_E_DEVICE_NOT_FOUND
zurückgegeben wird.
tf_hal_callback_tick
(TF_HAL *hal, uint32_t timeout_us)¶Pollt auf allen Geräten mit registriertem Callback-Handler nach Callbacks. Blockiert für die übergebene Zeit in Mikrosekunden.
Diese Funktion kann nicht-blockierend verwendet werden, indem sie mit einem Timeout von 0 aufgerufen wird. Die Bindings pollen dann ein einziges Gerät nach einem Callback, indem sie ein Byte über SPI senden und empfangen. Falls kein Callback verfügbar ist, wird die Funktion sofort beendet. Wenn das Gerät beginnt ein Callback zu schicken, wird es empfangen, bestätigt und der Callback-Handler wird ausgeführt.
Diese Funktion pollt mit einem Round-Robin-Scheduler über mehrere Aufrufe. Das heißt dass selbst wenn immer mit einem Timeout von 0 gepollt wird, alle Geräte so fair wie möglich abgefragt werden.
tf_hal_deadline_elapsed
(TF_HAL *hal, uint32_t deadline_us)¶Gibt true zurück, wenn die übergebene Deadline in Mikrosekunden abgelaufen ist, ansonsten false.
Robust gegen Überläufe bis zu UINT32_MAX / 2
.
tf_hal_get_error_counters
(TF_HAL *hal, char port_name, uint32_t *ret_spitfp_error_count_checksum, uint32_t *ret_spitfp_error_count_frame, uint32_t *ret_tfp_error_count_frame, uint32_t *ret_tfp_error_count_unexpected)¶Gibt die Fehlerzähler für den übergebenen Port zurück. Folgende Fehler werden gezählt:
spitfp_error_count_checksum
: Empfangene SPITFP-Pakete die wegen falscher Checksumme ignoriert wurdenspitfp_error_count_frame
: Empfangene SPITFP-Pakete mit invalider Längetfp_error_count_frame
: Empfangene TFP-Pakete mit invalider Längetfp_error_count_unexpected
: Empfangene TFP-Pakete die unerwartet waren, da sie auf unbekannte Anfragen antworten
tf_hal_log_error
(const char *format, ...)¶Loggt einen Fehler, falls das Log-Level in bindings/config.h
TF_LOG_LEVEL_ERROR
oder höher ist.
Unterstützt eine Teilmenge der normalen printf-Syntax. Siehe tf_hal_printf()
für Details.
tf_hal_log_info
(const char *format, ...)¶Loggt eine Information, falls das Log-Level in bindings/config.h
TF_LOG_LEVEL_INFO
oder höher ist.
Unterstützt eine Teilmenge der normalen printf-Syntax. Siehe tf_hal_printf()
für Details.
tf_hal_log_debug
(const char *format, ...)¶Loggt eine Debug-Meldung, falls das Log-Level in bindings/config.h
TF_LOG_LEVEL_DEBUG
oder höher ist.
Unterstützt eine Teilmenge der normalen printf-Syntax. Siehe tf_hal_printf()
für Details.
tf_hal_printf
(const char *format, ...)¶Diese Funktion ist eine minimalistische printf-Implementierung. Die folgenden Platzhalter werden unterstützt:
%[präfix]u
: Eine vorzeichenlose Ganzzahl in Basis 10%[präfix]d
: EIne vorzeichenbehaftete Ganzzahl in Basis 10%[präfix]b
: Eine vorzeichenlose Ganzzahl in Basis 2%[präfix]x
and %[präfix]X
: Eine vorzeichenlose Ganzzahl in Basis 16, in beiden Fällen mit Kleinbuchstaben.%c
: Ein einzelnes Zeichen%s
: Ein null-terminierter String%%
: Ein ProzentzeichenMit den Präfixen kann die Breite von Ganzzahlen kontrolliert werden.
Valide Präfixe sind I8
, I16
, I32
und I64
.
Zum Beispiel kann %I16x
als Platzhalter verwendet werden um eine 16-Bit Zahl hexadezimal auszugeben.
Padding, gruppierungen, l-Modifikatoren oder ähnliches, oder Floats werden nicht unterstützt.
Der Zeilenumbruch \n
wird in den oder die plattformspezifischen Zeilenumbruchs-Zeichen übersetzt.
tf_hal_strerror
(int e_code)¶Gibt eine Beschreibung für den übergebenen Fehlercode zurück.
tf_get_device_display_name
(uint16_t device_id)¶Gibt den Anzeigenamen für den übergebenen Device-Identifier zurück.