CtLab Library

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Die CtLab Library ist eine Klassenbibliothek für das c´t-Lab der Zeitschrift c´t im Allgemeinen und für das darauf basierende CtLab FPGA SigLab im Speziellen. Damit können Anwendungen erstellt werden, welche das c´t-Lab steuern können. Falls andere c´t-Lab-Geräte (d.h. Module wie z.B. DCG, DDS oder DIV) oder andere Anwendungen auf dem FPGA-Modul gesteuert werden sollen, sind Anpassungen vorzunehmen, die Komponente für das CtLab FPGA SigLab kann dabei als Beispiel genutzt werden.

Codebeispiele

Zu Beginn sollen hier einige Codeauszüge gezeigt werden, um die Verwendung der Bibliothek zu demonstrieren.

Anwendungen auf Geräteebene

Diese Beispiele zeigen die Verwendung gerätespezifischer Schnittstellen. Sie beziehen sich zwar auf das CtLab FPGA SigLab, es lassen sich aber mit wenig Aufwand ähnliche Programmierschnittstellen für andere c´t-Lab-Geräte (d.h. Module wie z.B. DCG, DDS oder DIV) schaffen. Die Basisfunktionalität ist vorhanden, die Implementierung für das CtLab FPGA SigLab kann als Beispiel für eigene Erweiterungen dienen.

Das folgende Beispiel erzeugt ein amplitudenmoduliertes Signal:

// Configure DDS channel 0 (carrier, modulated by DDS channel 1).
signalGenerator.DdsGenerators[0].Waveform = Waveforms.Sine;
signalGenerator.DdsGenerators[0].Frequency = 1000;
signalGenerator.DdsGenerators[0].Amplitude = (short)(signalGenerator.DdsGenerators[0].MaximumAmplitude * 1 / 2);
signalGenerator.DdsGenerators[0].AmplitudeModulationSource = ModulationAndSynchronizationSources.DdsGenerator1;

// Configure DDS channel 1 (modulator).
signalGenerator.DdsGenerators[1].Waveform = Waveforms.Sine;
signalGenerator.DdsGenerators[1].Frequency = 100;
signalGenerator.DdsGenerators[1].Amplitude = (short)(signalGenerator.DdsGenerators[1].MaximumAmplitude * 1 / 4);

// Flush all modifications, i.e. send all set commands that have modified values.
appliance.SendSetCommandsForModifiedValues();

Und so lässt sich der Universalzähler auf eine Frequenzmessung mit 100 ms Torzeit einstellen. Die Abfragekommandos für die Messwerte werden regelmäßig gesendet, bei einer Änderung des Messwertes wird das Ereignis ValueChanged ausgelöst.

// Configure the universal counter.
signalGenerator.UniversalCounter.PrescalerMode = PrescalerModes.GatePeriod_100ms;

// Flush all modifications, i.e. send all set commands that have modified values.
appliance.SendSetCommandsForModifiedValues();

// Listen to counter changes and display them.
signalGenerator.UniversalCounter.ValueChanged +=
    (sender, e) => Console.WriteLine("Counter reported a new frequency: {0}", e.Value);

// Send the cached query commands periodically.
appliance.StartSendingQueryCommands(_queryCommandSendPeriod);

Anwendungen auf Basisebene

Diese Beispiele zeigen die Verwendung der Schnittstellen auf Basisebene. Sie basieren auf Kommandos und Nachrichten und stellen die Grundlage für gerätespezifische Schnittstellen dar.

So lässt sich ein Kommando direkt erstellen und absenden. Diese Zeilen senden den Wert 128 an den Subkanal 1.

// Send a command.
var setCommandClass = new SetCommandClass(_channel, 1);
setCommandDictionary.Add(setCommandClass);
setCommandClass.SetValue(128);
setCommandDictionary.SendCommandsForModifiedValues();

Und so kann man per Ereignisbehandlung empfangene Nachrichten verarbeiten. Hier wird auf Nachrichten von Subkanal 255 reagiert.

// Prepare to receive messages.
var messageContainer = messageCache.Register(_channel, 255);
messageContainer.MessageUpdated +=
    (sender, e) => Console.WriteLine("Message received, channel {0}/{1}, raw value {2}",
                                     messageContainer.Message.Channel,
                                     messageContainer.Message.Subchannel,
                                     messageContainer.Message.RawValue);

Architektur

Die CtLab Library ist in mehrere Ebenen gegliedert, die wiederum die Aufteilung in Pakete beeinflusst haben. Diese Pakete sind über Schnittstellen miteinander verbunden. Implementierungen lassen sich mit geringen Änderungen austauschen. UML-Diagramme dieser Pakete können hier heruntergeladen werden:

Datei:CtLab Library Packages.pdf

Basispakete

Diese Pakete umfassen die vollständigen Implementierungen für einige grundlegende Ebenen (Verbindungen, Kommandos und Nachrichten) sowie Basismechanismen für weiterführende Ebenen (Geräte, Umgebung).

Verbindungsebene

Auf der Verbindungsebene wird das Versenden und Empfangen von Rohdaten (z.B. Zeichenketten) über die jeweils genutzte Schnittstelle gesteuert. Diese Rohdaten entsprechen auf den höheren Ebenen Befehlen und Nachrichten. Die darüberliegenden Ebenen arbeiten mit dieser Verbindungsebene, ohne sich um die konkrete physikalische Ausprägung (z.B. seriell, USB, LAN) kümmern zu müssen.

Es werden Implementierungen für (physikalische oder emulierte) serielle Ports sowie für simulierte Verbindungen mitgeliefert. Über emulierte serielle Ports wird auch der USB-Anschluss unterstützt. Die simulierte Verbindung ist für Tests ohne c´t-Lab-Hardware nützlich, sie kann gesendete Zeichenketten protokollieren sowie den Empfang von Zeichenketten simulieren.

Außerdem gibt es eine Implementierung für den Fall, dass die Hardware direkt über eine vorhandene SPI-Schnittstelle angesprochen werden soll. Das kann z.B. benutzt werden, wenn die CtLab Library auf einem Raspberry Pi läuft.

Ebene der Kommandos und Nachrichten

Auf dieser Ebene werden die Rohdaten der Verbindungsebene gekapselt und als gesendete Kommandos beziehungsweise empfangene Nachrichten abstrahiert. Das Assemblieren und Interpretieren der Rohdaten findet hier statt. Ein Cache verhindert das unnötige Mehrfachabsenden von Kommandos. Es kann außerdem ein Scheduler genutzt werden, welcher vorher registrierte Kommandos (z.B. zur Messwertabfrage) regelmäßig abschickt. Eintreffende Nachrichten lösen Ereignisse aus, die abonniert werden können.

Diese Ebene ist generisch realisiert und muss in der Regel nicht angepasst werden. Diese Standardimplementierung wird natürlich mitgeliefert.

Pakete der Geräteebene

Diese Pakete setzen auf den Basispaketen auf und führen geräteabhängige Konzepte ein.

Auf dieser Ebene werden die Befehle und Nachrichten gekapselt und als Gerätemanipulationen beziehungsweise Wertänderungen abgebildet. Gerätemanipulationen, z.B. das Verändern von Einstellungen, werden hier auf die entsprechenden Kommandos umgesetzt, empfangene Nachrichten auf Änderungen der zugeordneten Werte. Solche Wertänderungen lösen dann auf dieser Ebene wiederum Ereignisse aus, die abonniert werden können.

Diese Ebene bildet die einzelnen vorhandenen Geräte (d.h. die c´t-Lab-Module) ab. Die Basisimplementierung wird mitgeliefert, ebenso eine konkrete Implementierung für ein als CtLab FPGA SigLab konfiguriertes FPGA-Modul. Mit letzterer kann man die auf dem CtLab FPGA SigLab vorhandenen DDS- und Pulsgeneratoren sowie den Universalzähler intuitiv steuern.

Pakete der Umgebungsebene

Diese Pakete setzen auf den vorgenannten auf und berücksichtigen den konkreten Aufbau einer c´t-Lab-Arbeitsumgebung, d.h. die Ausstattung mit Modulen wie z.B. DCG, DDS oder DIV und deren konkrete Konfiguration.

Diese Ebene repräsentiert die als Hardware vorhandene Arbeitsumgebung, d.h. die konkrete Bestückung des c´t-Lab mit Geräten (Modulen). Hier werden die verwendeten c´t-Lab-Module und deren Zuordnung zu den verfügbaren Kanälen berücksichtigt. Diese Ebene stellt auch den zentralen Einstiegspunkt der Bibliothek zur Verfügung.

Hinweise zur Realisierung

Die CtLab Library wurde mit Mono und MonoDevelop auf Ubuntu 16.04 in C# entwickelt. Sie lässt sich aber ohne Änderungen ebenso unter Windows in Visual Studio laden, übersetzen und ausführen (zuletzt getestet mit Visual Studio 2012 unter Windows 7). Auch auf dem Raspberry Pi ist die Software einsetzbar und kann auch direkt dort übersetzt werden.

Dependency Injection mit StructureMap

Zur besseren Modularisierung wird Dependency Injection angewendet und dafür der Dependency-Injection-Container StructureMap eingesetzt. Auf diese Weise werden die einzelnen Ebenen streng getrennt und nur über Schnittstellen verbunden. Die konkrete Implementierung einer Schnittstelle wird sehr spät festgelegt und kann auch mit wenig Aufwand geändert werden. Das hat nicht zuletzt Vorteile bei den automatisierten Tests und wird dort intensiv genutzt.

Automatisierte Unit-Tests mit SpecsFor

Die gesamte Entwicklung erfolgte testgetrieben unter intensivem Einsatz automatisierter Unit-Tests. Für die Erstellung der Unit-Tests, die gleichzeitig auch als Spezifikation dienen, wurde das Komplettpaket SpecsFor eingesetzt. Wie bei TDD üblich, wurde die Namensgebung der Testklassen und -methoden so gewählt, dass sich mehr oder weniger natürlichsprachige Anforderungen ergeben, deren Einhaltung der jeweilige Test dann überprüft.

Die gesamte Testsuite nutzt die simulierte Verbindung und kann daher ohne vorhandene c´t-Lab-Hardware durchlaufen werden. Sie enthält einerseits Tests, welche isolierte Klassen und Ebenen testen. Andere Tests überprüfen aber auch die Integration und die korrekte Zusammenschaltung über den Dependency-Injection-Container StructureMap.

Quellcode, Lizenz

Die CtLab Library ist quelloffen (Open Source) und wird unter der GNU General Public License (GPL) veröffentlicht. Der Quellcode steht auf GitHub zur Verfügung.

Externe Komponenten werden über die Paketverwaltung NuGet bezogen. Das geschieht beim ersten Öffnen der Projektmappe in MonoDevelop automatisch.

Unter "Library/Test Console" befindet sich der Quellcode für eine kleine Consolenanwendung, welche mehrere Beispiele enthält. Diese zeigen die Anwendung der CtLab Library für verschiedene Aufgaben. Einige dieser Beispiele funktionieren ohne vorhandene c´t-Lab-Hardware, andere setzen ein CtLab FPGA SigLab voraus.

Weitere Informationen

Quellcode:

Die CtLab Library eigent sich gut für die Steuerung des CtLab FPGA SigLab. Näheres gibt es unter:

Das c´t-Lab ist ein Projekt von Carsten Meyer, c't magazin. Weitere Informationen gibt es hier: