C/C++ für Mikrocontroller - API Bindings

Die C/C++ Bindings ermöglichen es Bricks und Bricklets mit selbst erstellen C/C++ Programmen, die auf einem Mikrocontroller laufen, zu steuern. Die ZIP Datei für die Bindings beinhaltet:

  • in source/bindings den Quelltext der Bindings
  • in source/hal_* den Quelltext der HALs für einige Plattformen
  • in examples/ die Beispiele für alle Bricks und Bricklets

Unterstützte Hardware

Die C/C++ Bindings für Mikrocontroller unterstützen Co-Prozessor-Bricklets, also diese mit einem 7-Pol-Anschluss, sowie das HAT Brick und das HAT Zero Brick. Die älteren Brickets mit 10-Pol-Anschluss, sowie andere Bricks werden nicht unterstützt. Siehe hier für eine vollständige Liste unterstützter Geräte.

Das C/C++-Programm, das die Bindings verwendet läuft auf dem Host. Als Host unterstützt werden aktuelle der ESP32 Brick und der ESP32 Ethernet Brick. In Zukunft werden weitere Mikrocontroller als Host unterstützt werden.

Die Bindings benötigen einen host-spezifischen Hardware Abstraction Layer (HAL). Siehe hier für Details.

Voraussetzungen

  • C-Compiler (C99-kompatibel) oder C++-Compiler

Installation

Da es keine vorkompilierte Bibliothek für die C/C++ Bindings für Mikrocontroller gibt, gibt es in diese Sinne auch nichts zu installieren. Die empfohlene Art und Weise die Bindings zu verwenden, ist ihren Quelltext direkt in das jeweilige C/C++ Projekt mit einzubinden.

Test eines Beispiels

Die Beispiele für jeden unterstützten Brick und Bricklet sind nicht vollständig, da diese von der verwendeten Host-Hardware (und deren HAL) abhängen. Viele HALs beinhalten einen Beispiel-Treiber der jegliches Beispiel ausführen kann. Weitere Details sind in den spezifischen HAL Dokumentationen zu finden.

UID oder Port-Name

Alle Geräte-*_create-Funktionen nehmen ein uid_or_port_name Parameter, das es dem Programm ermöglicht festzulegen mit welchem Gerät kommuniziert werden soll. Dies kann auf drei Weise erfolgen:

  • Bei Übergabe von NULL wird das erste unbenutzte Gerät des entsprechenden Typs ausgewählt unabhängig von UID und Port-Name.
  • Bei Übergabe einer UID (z.B. "XyZ") wird das erste unbenutzte Gerät des entsprechenden Typs mit passender UID ausgewählt.
  • Bei Übergabe eines Port-Names (z.B. "A") wird das erste unbenutzte Gerät des entsprechenden Typs mit passendem Port-Namen ausgewählt.

In diesem Fall bedeutet unbenutzt, dass das Gerät keinem Device-Objekt zugeordnet ist. Zum Beispiel sind zwei Temperature Bricklet 2.0 angeschlossen, eins an Port A und eins an Port B. Bei Übergabe von NULL wählt der erste tf_temperature_v2_create Aufruf das Bricklet an Port A aus. Der zweite Aufruf wählt das Bricklet an Port B, da das Bricklet an Port A bereits benutzt wird.

Hardware Abstraction Layer

Die C/C++ Bindings für Mikrocontroller funktionieren anders als die anderen verfügbaren Bindings. Während andere Bindings, die über TCP/IP mit einem Brick Daemon kommunizieren, eine IP Connection verwenden, benötigen die C/C++ Bindings für Mikrocontroller einen Hardware Abstraction Layer (HAL). Der HAL abstrahiert den plattformspezifischen Weg der SPI-Kommunikation.

Die Bindings beinhalten HALs für die folgenden Plattformen. Für andere Plattformen muss ein eigener HAL implementiert werden (siehe unten).

Implementieren eines eigenen HALs

Falls die Bindings auf einer anderen Plattform verwendet werden sollen, muss ein eigener HAL implementiert werden. Diese Anleitung zeigt wie.

Callbacks

Callbacks funktionieren in den C/C++ Bindings für Mikrocontroller anders als in anderen Bindings. Da es auf vielen der Zielplattformen kein Multi-Threading gibt, wird nicht automatisch nach Callbacks gepollt. Stattdessen muss, wenn Callbacks empfangen werden sollen, mit der in jedem HAL verfügbaren tf_hal_callback_tick-Funktion nach Callbacks gepollt werden. Diese Funktion pollt nur angeschlossene Geräte, für die mindestens ein Callback-Handler registriert ist.

Callback werden nicht von einem anderen Thread ausgeliefert, sondern nur, während ein Getter, Setter oder die tf_hal_callback_tick-Funktion ausgeführt wird. Die Bindings sind deshalb während der Ausführung eines Callback-Handlers in einem Zustand, der es nicht erlaubt weitere Pakete zu schicken. Es ist nicht erlaubt, Getter, Setter oder Tick-Funktionen aus einem Callback-Handler aufzurufen

Threadsicherheit

Die primäre Zielplatform für die Bindings sind Mikrocontroller, deshalb sind sie nicht thread-sicher. Einige HALs unterstützen kooperatives Multitasking, es sind aber keine Aufrufe der Bindings-API erlaubt, während sie pausiert sind. Alle Funktionen geben während die Bindings pausiert sind, oder gerade ein Callback ausgeliefert wird, TF_E_LOCKED zurück.

Performanceoptimierungen

Um die beste Performance aus den Bindings zu holen, kann der folgenden Liste von Optimierungen gefolgt werden:

  • Erhöhen der SPI-Taktfrequenz von 1,4 MHz auf so nah wie möglich an 2 MHz. Eine zu hohe Taktfrequenz wird die Performance aber wieder verschlechtern, da die angeschlossenen Geräte die Daten nicht mehr empfangen können. In unseren Tests wurde die optimale Performance bei 1,95 bis 1,96 MHz erreicht, aber das ist stark abhängig vom Aufbau, dem HAL und der Stabilität der SPI Clock des Host-Systems.

    Die SPI-Taktfrequenz kann in den HAL-Implementierungen eingestellt werden.

  • Bevorzugung von Callbacks. Die Bindings können ein angeschlossenes Gerät nach verfügbaren Callbacks fragen, indem ein einzelnes Byte über SPI geschickt wird. Wenn Getter verwendet werden um schnell veränderlichen Zustand abzufragen, muss jedes Mal eine komplette TFP-Anfrage, deren Antwort und ein Acknowledgment über SPI verschickt werden, was mindestens 23 Byte Datenverkehr bedeutet.

  • Wenn exakt bekannt ist, welche Callbacks in welchen Intervallen eintreffen werden, sollte nicht tf_hal_callback_tick verwendet werden, sondern ein eigenes Scheduling für das Pollen mit den gerätespezifischen tf_[device]_callback_tick-Funktionen verwendet werden. Das erlaubt es, mehr Zeit für andere Aufgaben aufzuwenden. tf_hal_callback_tick verwendet einen Round-Robin-Scheduler um alle Geräte mit einem registrierten Callback Handler zu pollen.

  • Falls mehrere SPI-Einheiten verfügbar sind, können, soweit unterstützt, mehrere HAL-Instanzen verwendet werden. Das erlaubt es, mit mehreren Geräten parallel zu kommunizieren. Jede HAL-Instanz darf trotzdem nur von einem Thread verwendet werden.

RAM und Flashgröße optimieren

Damit eine Firmware mit den Bindings auf kleinere Plattformen wie den Arduino Uno passt, können die folgenden Konfigurationsoptionen in bindings/config.h angepasst werden:

  • TF_INVENTORY_SIZE: Die Größe des Inventory, also des Mappings von UIDs auf die Ports unter denen die Geräte erreichbar sind. Wenn pekannt ist, mit wie vielen Geräten kommuniziert werden soll, kann die Größe auf diesen Wert gesetzt werden. Die HAL-Initialisierung gibt TF_E_TOO_MANY_DEVICES zurück, falls das Inventory zu klein war.
  • TF_IMPLEMENT_CALLBACKS: Falls keine Callbacks benutzt werden sollen, kann dieses define entfernt werden, damit der entsprechende Code nicht verwendet wird
  • TF_LOG_LEVEL: Durch reduzieren des Log-Levels werden viele String-Konstanten nicht einkompiliert. Valide Werte sind:
    • TF_LOG_LEVEL_NONE: Deaktiviert das Logging komplett
    • TF_LOG_LEVEL_ERROR: Loggt nur HAL-spezifische Fehler wenn sie auftreten
    • TF_LOG_LEVEL_INFO: Loggt zusätzlich die Liste der gefundenen Geräte bei der HAL-Initialisierung
    • TF_LOG_LEVEL_DEBUG: Loggt zusätzlich alle internen Zustandsänderungen der SPITFP-Zustandsmaschine
  • TF_IMPLEMENT_STRERROR: Wenn dieses define entfernt wird, wird die tf_hal_strerror-Funktion nicht implementiert. Damit können ungefähr 500 Bytes Speicherplatz freigemacht werden.

API Referenz und Beispiele

Links zur API Referenz der HALs, Bricks und Bricklets sowie die Beispiele aus der ZIP Datei der Bindings sind in der folgenden Tabelle aufgelistet. Anleitungen für weiterführende Projekte finden sich im Abschnitt über Kits.

  API Beispiele
Sonstiges    
HAL Arduino API Beispiele
HAL Arduino ESP32 API Beispiele
HAL Arduino ESP32 Brick API Beispiele
HAL Arduino ESP32 Ethernet Brick API Beispiele
HAL Linux API Beispiele
HAL Raspberry Pi API Beispiele
     
Bricks    
HAT API Beispiele
HAT Zero API Beispiele
     
Bricklets    
Accelerometer 2.0 API Beispiele
Air Quality API Beispiele
Ambient Light 3.0 API Beispiele
Analog In 3.0 API Beispiele
Analog Out 3.0 API Beispiele
Barometer 2.0 API Beispiele
CAN 2.0 API Beispiele
CO2 2.0 API Beispiele
Color 2.0 API Beispiele
Compass API Beispiele
DC 2.0 API Beispiele
Distance IR 2.0 API Beispiele
Distance US 2.0 API Beispiele
DMX API Beispiele
Dual Button 2.0 API Beispiele
E-Paper 296x128 API Beispiele
Energy Monitor API Beispiele
GPS 2.0 API Beispiele
GPS 3.0 API Beispiele
Hall Effect 2.0 API Beispiele
Humidity 2.0 API Beispiele
IMU 3.0 API Beispiele
Industrial Analog Out 2.0 API Beispiele
Industrial Counter API Beispiele
Industrial Digital In 4 2.0 API Beispiele
Industrial Digital Out 4 2.0 API Beispiele
Industrial Dual 0-20mA 2.0 API Beispiele
Industrial Dual AC Relay API Beispiele
Industrial Dual Analog In 2.0 API Beispiele
Industrial Dual Relay API Beispiele
Industrial PTC API Beispiele
Industrial Quad Relay 2.0 API Beispiele
IO-16 2.0 API Beispiele
IO-4 2.0 API Beispiele
Isolator API Beispiele
Joystick 2.0 API Beispiele
Laser Range Finder 2.0 API Beispiele
LCD 128x64 API Beispiele
LED Strip 2.0 API Beispiele
Linear Poti 2.0 API Beispiele
Load Cell 2.0 API Beispiele
Motion Detector 2.0 API Beispiele
Motorized Linear Poti API Beispiele
Multi Touch 2.0 API Beispiele
NFC API Beispiele
OLED 128x64 2.0 API Beispiele
One Wire API Beispiele
Outdoor Weather API Beispiele
Particulate Matter API Beispiele
Performance DC API Beispiele
Piezo Speaker 2.0 API Beispiele
Real-Time Clock 2.0 API Beispiele
Remote Switch 2.0 API Beispiele
RGB LED 2.0 API Beispiele
RGB LED Button API Beispiele
Rotary Encoder 2.0 API Beispiele
Rotary Poti 2.0 API Beispiele
RS232 2.0 API Beispiele
RS485 API Beispiele
Segment Display 4x7 2.0 API Beispiele
Servo 2.0 API Beispiele
Silent Stepper 2.0 API Beispiele
Solid State Relay 2.0 API Beispiele
Sound Pressure Level API Beispiele
Temperature 2.0 API Beispiele
Temperature IR 2.0 API Beispiele
Thermal Imaging API Beispiele
Thermocouple 2.0 API Beispiele
UV Light 2.0 API Beispiele
Voltage/Current 2.0 API Beispiele
XMC1400 Breakout API Beispiele
     
Bricklets (Abgekündigt)    
PTC 2.0 API Beispiele
RGB LED Matrix API