Experience Embedded

Professionelle Schulungen, Beratung und Projektunterstützung

OO und modellbasierte Werkzeuge

Autoren: Dr. Andreas Wagener, Dr. Fritz Faulhaber GmbH & Co. KG, Robert Stemplinger und Markus Pauls, b1 Engineering Solutions GmbH

Beitrag - Embedded Software Engineering Kongress 2015

 

Über Tools wie Rhapsody kann aus der Modellierung direkt Code auch für Embedded Systeme generiert werden. Im betrachteten Projekt wird das gesamte System derart in Rhapsody entworfen und implementiert. Stacks und signalverarbeitender Code werden mit eingebunden. Dazu sind einige Vorarbeiten nötig, das Abstraktionsniveau steigt für alle Beteiligten - verbunden mit der Chance, bis zum Produkt eine klar erkennbare Architektur umzusetzen und damit ein gut wartbares Produkt zu erhalten.

Die Dr. Fritz Faulhaber GmbH & Co. KG entwickelt, fertigt und verkauft Klein- und Micromotoren, die dazu passenden Positionsgeber und Ansteuerungen zum geregelten Betrieb der Motoren. Die Ansteuerungen sind in Ausführungen als reine Drehzahlregler oder auch als Positionsteuerungen im Programm. Realisiert sind diese als eingebettete Steuerungen auf der Basis von µControllern, in denen alle Stufen der Regelung rein digital abgebildet werden. Betrachtet werden sollen hier die Positioniersysteme.

Die Software einer Motoransteuerung für einen Servoantrieb kann grob in die Teile

  • Hardwaretreiber,
  • Motorregelung,
  • Steuerung und Diagnose,
  • Kommunikation und
  • Parametersprecher (Objekt-Verzeichnis oder Management Information Base (MIB))

 

gegliedert werden. Erweiterungen und Anpassungen an veränderte Anforderungen, aber auch Beschränkungen des Zielsystems erforderten in den Bestandssystemem den klaren Entwurf zu verlassen. Dies führt zu der klassischen Situation bei eingebetteten Steuergeräten mit deutlich ineinander übergehenden Softwareanteilen.

Trotz dieser Beschränkungen stellt der Motion Controller ein sehr stabiles Gerät dar, Erweiterungen erfordern jedoch einen höheren Aufwand und Entwicklungszeitraum. Insofern war für eine nächste Ansteuerungs-Generation klar, dass insbesondere die Wartbarkeit und Erweitbarkeit durch eine klare und modulare Software-Architektur deutlich verbessert werden sollte.

Funktionale Anforderungen waren:

  • ein deutlich gesteigerter Umfang an unterstützenden Betriebsarten,
  • die Unterstützung mindestens eines Ethernet-basierten Feldbusses als Option,
  • eine wesentlich gesteigerte Flexibilität in der Konfiguration.

 

Die Anforderungen waren mit der Erwartung verbunden, künftig wieder deutlich schneller auf Kundenwünsche eingehen zu können.

Gleichzeitig sollte der Wechsel auf eine aktuelle und zukunftsträchtige Prozessorfamilie erfolgen, die Software aber so entwickelt werden, dass auch künftige Prozessorwechsel deutlich vereinfacht werden.

Grundentscheidungen

Aufgrund der sehr verwobenen Struktur nach Abbildung 1 (siehe PDF) war relativ schnell klar, das aus dem bestehenden Produkt eigentlich nur die Erfahrungen im Betrieb von elektrischen Kleinantrieben übernommen werden konnten. Alle Teile der Software mussten neu entworfen werden.

Eine Aufgabe war daher die Suche nach einer Prozessorplattform für die speziellen Anforderungen von integrierbaren Kleinantrieben. Die andere Aufgabe war die Suche nach einer geeigneten Entwurfs-Methodik, verbunden mit der Suche nach geeigneten Werkzeugen, die es erlauben würden, die Grundentscheidungen aus dem Entwurf auch in der Implementierung abzusichern.

Abweichend vom bisherigen Vorgehen sollte ein objekorientierter Ansatz verfolgt werden, auch um den Entwurf über die Mechanismen der UML zu strukturieren.

Bereits bisher kamen für die Entwicklung der signalverarbeitenden Softwareteile in der Regelung und Positionsauswertung Target Link für die Code-Erzeugung aus SIMULINK Modellen zum Einsatz. Dieser Ansatz eignet sich aber nicht, um die eher event-basierten Softwareteile aus der Treiber-Schicht, dem Parameterhandling, der Kommunikation und auch der Antriebssteuerung zu beschreiben und dann auch zu implementieren.

Nach einer ersten Evaluation und auf der Basis der Erfahrungen aus früheren Projekten [1] fiel die Entscheidung auf Rhapsody bzw. für die Embedded Version, wie sie von der Fa. Willert angeboten wird. Hauptargumente waren:

  • Arbeitet nativ im OO-Entwurf
  • Unterstützung von typischen Embedded Plattformen auch in C bei guter Lesbarkeit
  • Ausreichende Durchgängigkeit vom Entwurf bis in die Implementierung
  • Guter Einstieg möglich, da wenig Legacy Anteile zu übernehmen

 

Es war aber von Beginn an klar, dass damit auch eine erhebliche Lernkurve sowohl hinsichtlich der Bediensicherheit des Werkzeugs als auch in der Entwurfsmethodik anstehen würde. Klar war daher auch, dass wir uns einen erfahrenen Partner für die anstehenden Kernthemen suchen mussten, den wir in der Fa. Tieto bzw. Tieto Embedded Systems - heute b1 Engineering Solution fanden (siehe dazu auch Abb. 2, PDF).

Architektur & Design

Basierend auf der erarbeiteten Spezifikation, den Use-Cases des Systems und den Erkenntnissen aus dem Bestandssystem entstand zunächst eine Grobarchitektur:

  • Aufteilung der Gesamtfunktion in Pakete und z.T. auch schon in Objekte
  • Definition der treibenden Aktivitäten des Systems
  • Klärung der Datenverwaltung bzgl. des Umgangs mit den Parametern und deren Speicherung

Umsetzung

Angewandte Muster waren die Schichtbildung (Layering) mit insbesondere einer eindeutigen Zuordnung von Verantwortlichkeiten für die Kapselung der µController-typischen Peripherieeinheiten im HAL und einer darauf aufsetzenden Middleware-Schicht (siehe Abb. 3, PDF). Die Middleware-Schicht stellt deutlich abstrahierte Informationen wie die Motorgeschwindigkeit als Ergebnis unterschiedlichster Sensorsysteme zur Verfügung oder errechnet Stellgrößen aus allgemeinen Vorgaben, wie z.B. die passende Ansteuerung für den angeschlossenen Motor aus der Sollspannung des Reglers.

Um eine Signalflussrichtung entgegen den definierten Abhängigkeitsbeziehungen zu realisieren, wurde das Publish/Subscribe-Pattern bzw. Beobachter-Muster verwendet. Dazu registrieren Informationskonsumenten aus höheren Schichten Callback-Funktionen und Instanz-Zeiger bei den Informationsproduzenten in den niedrigeren Schichten. Dieses Pattern wird gleichermaßen für den Übergang zwischen Interrupt-Kontext und Task-Kontext als auch zwischen Task-Kontexten verwendet. Um die Nebenläufigkeiten zu beherrschen, wird in den Callback-Funktionen daher im Allgemeinen ein Event der Laufzeitumgebung an die adressierte Objekt-Instanz versendet. Es ist vielfach ausreichend, genau einen Subscriber bereits während der Initialisierung zu registrieren (siehe Abb. 4, PDF).

Die Aktivitäten des Systems können aufgeteilt werden in die streng zeitlich getriebenen Aktivitäten einerseits:

  • der Timerinterrupt für die Motorregelung (100µs, synchron zu PWM)
  • ein Timerinterrupt mit 1ms Zykluszeit für Diagnoseaufgaben

 

Beide werden über einen Interrupt angestoßen und auch im Interrupt-Kontext ausgeführt. Diese signalverarbeitenden Systemteile werden in SIMULINK entworfen und über TargetLink in C-Code mit einer zum Systementwurf passenden Schnittstellengestaltung überführt.

Auf der anderen Seite stehen die eher reaktiven Aktivitäten:

  • Externe Ereignisse, wie z.B. der Empfang einer kompletten Botschaft aus den Kommunikations-Stacks (die Verarbeitung der einzelnen Zeichen, z.B. der RS232 Schnittstelle, werden komplett im Schnittstelleninterrupt-Kontext ausgeführt).
  • Time-outs über einen niedriger priorisierten Timer

 

In beiden Fällen wird der Interrupt-Kontext verlassen und ein Event ausgelöst, der typisch über eine Zustandsmaschine bearbeitet wird. Die Modellierung der Interaktion kann hier vorteilhaft über Kommunikationsidagramme, die Systemreaktion über Zustandsmaschinen entworfen werden.

Anteile der Laufzeitumgebung

Innerhalb des Willert Framework werden Zustandsmaschinen und die nötige Eventbehandlung direkt nativ angeboten, d.h. aus Zustandsdiagrammen und den dazu gehörenden Events kann direkt lauffähiger Code erzeugt werden. Da die Reaktion auf jeden Event am Stück abgearbeitet wird (run to completion), können Zustandsmaschinen auch verwendet werden, um quasi gleichzeitige Ereignisse zu serialisieren, indem die Ereignisse in einer Event-Queue einer Zustandsmaschine eingestellt werden – so z.B. für die Bearbeitung von über die serielle Schnittstelle erhaltener Anfragen in Konkurrenz zu intern erzeugten asynchronen Botschaften.

Umsetzung in Rhapsody

Das Gesamtsystem wurde in eine Reihe von parallelen Rhapsody Projekten zerlegt, die je für eine Komponente die Arbeitsumgebung aus Implementierung und Testpaket beinhalteten. Damit können die Komponenten weitgehend getrennt bearbeitet werden - wichtig für die Arbeit im Team.

Die Komponenten wurden dann entsprechend den Abhängigkeiten und Schichten von Beginn an zu Integration zusammengefügt (Top Down), deren Funktionsumfang dann schrittweise wachsen konnte.

Die Basis bildet ein Projekt, in dem lediglich Typ- und Stereotype-Definitionen beheimatet sind, die sich aus der Programmierrichtlinie ergeben. Darauf setzten die Fachpakte auf, die je aus der zu entwickelnden Komponente und optional einem Test-Package bestehen. Über das Test-Package können aus dem Fachpaket ausführbare Testkomponenten erzeugt werden, die je nur den Umfang der betrachteten Fachkomponente nutzen. Auf oberster Ebene werden die Komponenten im Integrationsprojekt referenziert (siehe Abb. 5, PDF-Datei)

Integration von C-Code

Für die Integration und die zu erstellenden Binärfiles stellen die Rhapsody-Projekte die Build-Umgebung dar. Der gesamte Code wird über die Projekte in ein Projekt der C-Entwicklungsumgebung (hier Keil µVision) verschoben und dort nur gebildet und debuggt. Die exportierten C-Files unterliegen damit nicht dem Konfigurationsmanagement.

Trotzdem müssen auch in C vorliegende Code-Teile, wie z.B. gekaufte Kommunikations-Stacks oder die über TargetLink erzeugten Code-Anteile, integriert werden. Aufgaben sind dabei, die vorhandenen Codes jeweils aktuell ins C-Projekt zu kopieren, sowie ggf. die Gestaltung von Schnittstellen, um die externen Code-Anteile im Projekt nutzen zu können.

Der erste Teil der Aufgabe gelingt über make-File Erweiterungen, die innerhalb der in Rhapsody erstellten Komponenten angelegt werden können und die Kopieranweisungen aufnehmen.

Für die Integration ins Modell wurden typisch Wrapper-Klassen angelegt. So kann z.B. mit TargetLink sehr gut ein Regler erstellt werden. Auch können die Parameter und Variablen in je einer klassenartigen Struktur gesammelt und auch an der Funktionsschnittstelle der eigentlichen Rechenroutine verwendet werden. Das so erzeugte C-Modul kann dann als externer Code im Projekt eingebunden werden. Für den Zugriff auf die einzelnen Parameter der Regler-Struktur werden in TargetLink aber keine zur Integration geeigneten Zugriffsmethoden bereitgestellt. Bliebe der Direktzugriff auf die Member der Struktur, oder eben ein Wrapper, der dafür die Read- und Write-Handler bereitstellt und das Wissen über die C-Struktur kapselt.

Vorteile

Das Projekt ist noch nicht abgeschlossen, eine vorläufige Bewertung lässt als Vorteile erkennen:

  • Durch die Strukturierung des Systems über die Mechanismen der OO bleibt der Grundentwurf der Architektur ständig im Blick. Abhängigkeiten werden bewusst gezogen, das Schichtenmodell konsequent eingehalten. 
  • Bei sauberer Trennung zwischen den zeitgetrieben und den eventgetriebenen Aufgaben des Systems können die Mechanismen der in der Laufzeitumgebung hinterlegten Event-Queues voll zur Geltung kommen. Trotz einer Reihe von Nebenläufigkeiten sind die Entwickler nicht mehr mit den zur Synchronsiation nötigen Mechanismen wie Semaphoren und Interrupt-Sperren befasst.
  • Die Lesbarkeit des erzeugten Codes ist grundsätzlich hoch, es bleibt aber die Aufgabe der Entwickler bzw. des Projektleiters darauf zu achten, dass die Komplexität der Methoden und Klassen beherrschbar bleibt. Das Werkzeug leistet dabei keine Unterstützung.

 

Herausforderungen und Mühen

Insbesondere aus Sicht des Entwicklers ergeben sich in der täglichen Anwendung von Rhapsody einige Hürden, mit denen man zu leben lernen muss. Im Folgenden seien einige Beispiele genannt. Vieles könnte unserer Meinung nach leicht abgestellt werden, wenn der Wille zur Pflege des Produkts noch ausgeprägter wäre.

Der RPY Code- und Modell-Editor

  • Das Kopieren/Einfügen/Modifizieren (ein häufig angewandtes Entwicklungsmuster) von Modellelementen geht zwar häufig, aber nicht immer. Probleme treten insbesondere bei den Elementen unterhalb des Standard-Packages "Components" auf oder aber auch beim Kopieren zwischen verschiedenen Projekten.
  • Ein Kopieren aus ReadOnly-Fenstern (z.B. in eine Mail) ist nicht möglich. Auch kann man in ReadOnly-Fenstern nicht scrollen.
  • Windows Key-Belegung (Ctrl-Entf, Ctrl-Einfg) funktioniert nicht, nur Ctrl-C und Ctrl-V.
  • Autovervollständigung im Code-Editor funktioniert nur unvollständig (nur einmal pro Zeile, nur lokale Elemente, keine Literale, ...)
  • Generierter Code, der einem Package zuzuordnen ist, kann nicht automatisch mit einem Prefix versehen werden.
  • Refactoring ist mühsam, da der händisch hinterlegte Code vom Tool nicht mit einbezogen wird.

 

Properties

Welche Properties bei der Code-Generierung wie oder wann wirken, ist sehr undurchsichtig. Hier hilft oft nur Probieren. Beispielsweise ist das Property C_CG::Type::ReturnType nicht wirksam, wenn man sich über das Property C_CG::Attribute::AccessorGenerate automatisch einen Accessor generieren lassen will. Im genannten Fall sollte anstelle einer Kopie einer Struktur (default) ein Const-Zeiger auf die Struktur zurückgegeben werden.

Versionierung

Die Files, in denen Rhapsody seine Informationen hinterlegt, sind leider nicht sehr "Merge"-freundlich gestaltet. Insbesondere werden in Files häufig Zeitstempel und GUIDs geändert, obwohl sie ansonsten keinerlei Änderungen erfahren haben. Um ein gemeinsames Arbeiten zu ermöglichen, haben wir daher häufig vor dem Commit mit den Mitteln des Konfigurations-Management-Tools manuell kontrolliert, welche Files wirklich inhaltlich geändert wurden. Bei tatsächlichen Konflikten war mitunter auch Handarbeit mit dem Merge-Tool notwendig, sofern sich diese Konflikte nicht im Vorfeld durch Absprachen umgehen ließen.

OO in C

Wir arbeiten gerne und häufig mit konstanten, privaten Daten-Tabellen, die dann durch den Code ausgewertet werden. Leider können Staircase-Member bei Klassen nicht angelegt werden (wie es in C++ möglich ist). Anwendungsbeispiele wären:

  • Tabellen-gestützter CRC-Algorithmus
  • HW-Konfigurationen
  • Tabellen von Callback-Funktionen

 

Umgehungsmöglichkeiten ergeben sich teilweise durch die Definitionen durch Types, aber z.B. nicht für die Callback-Funktionen, da die Funktionsprototypen im generierten Code erst nach den Types angeordnet werden.

Voraussetzungen

Die Erfahrung zeigt, dass für ein effizientes Arbeiten mit Rhapsody frühzeitig die richtigen Voraussetzungen zu schaffen sind. Aus unserer Sicht sind folgende Punkte wichtig:

  • Für die projektspezifischen Einstellungen sollte von Beginn an ein Profil angelegt und durchgängig referenziert werden (siehe Abbildung 5, PDF). Da es am Anfang aufgrund fehlender Erfahrung meist noch unklar ist, welche Property-Einstellungen wie zu setzen ist, lassen sich auf diese Art und Weise ansonsten weitreichende nachträgliche Änderungen relativ einfach einbringen.
  • Bestimmte Property-Einstellungen sollten an Stereotypes gebunden werden. Sie sind dann deutlich im Modell erkennbar und können einheitlich gehandhabt werden. Beispiele:
    • SimpleType: z.B. für enums, die sollen im Allgemeinen per Value übergeben werden.
    • Volatile: setzt das Property C_CG::Attribute::PreDeclarationModifier zu “volatile” und sollte für volatile Attribute verwendet werden.
    • PackedT: setzt des Property C_CG::Type::PreDeclarationModifier zu “__packed” und sollte für gepackte Strukturen verwendet werden.
  • Packages sollten großzügig verwendet werden, um die Zusammenarbeit mehrerer Entwickler zu erleichtern. Diese Packages können dann in den jeweiligen Entwickler-Projekten referenziert werden. Packages werden standardmäßig als eigene Unit in einem separaten File gespeichert.
  • Es ist vorteilhaft, ein "Helper"-Package mit allgemeingültigen Typen und Algorithmen (z.B. CRC) ganz unten im Layering zu definieren, das für alle darüber liegenden Layern wiederverwendbar ist. Das hatten wir – abgesehen von den üblichen Integer-Datentypen – nicht gemacht. Dies später nachzuholen, ist nur noch mit viel Aufwand realisierbar.

 

Unabhängig von der konkreten Entwicklungsmethodik ist ein gutes Grundverständnis der Domänen-spezifischen Probleme immer ein wesentlicher Erfolgsfaktor. In diesem Falle ist es z.B. das Verständnis der treibenden Aktivitäten und die Zuordnung in den zeitgetriebenen Interrupt-Kontext oder den ereignisbasierten Task-Kontext.

Durch den abstrakteren, modellhaften Ansatz muss von den beteiligten Entwicklern noch mehr entsprechendes Wissen abverlangt werden. Man darf sich bei der Methodik nicht gleich in die Bits und Bytes flüchten und im Nachhinein schauen, wie man alles zusammenfügt. Der Vorteil von entsprechenden Modellierungs- und Codier-Richtlinien muss sicherlich nicht extra hervorgehoben werden.

Ergebnisse

Die bisherigen Ergebnisse müssen differenziert betrachtet werden. Aus Sicht des Architekten erlaubt die modellbasierte Arbeit einen zügigen Übergang von der Architektur bis in die Implementierung. Insbesondere bleiben durch die expliziten Notationen die Grundentscheidungen der Architektur stets im Blick.

Für die Entwickler stellt der Wechsel auf OO und Rhapsody eine erhebliche Umstelung ihrer Arbeitsweise dar. Statt in C-Files und sehr ausgefeilten C-Editoren entsteht der Code im Editor von Rhapsody. Änderungen in den C-Files während des Tests müssen sorgfältig zurückgepflegt werden. Nicht unterschätzt werden darf auch der Paradigmenwechsel von einer Main-Loop auf ene Event-basierte Systemarchitektur.

Aus Sicht des Projektverantwortlichen bestand die größte Herausforderung in der Suche nach geeigneten Partnern. Der Umstieg auf einen OO-Entwurf gelingt nicht aus dem Stand. Rhapsody stellt ein sehr mächtiges Werkzeug dar, mit eher überschaubarer Dokumentation. Trotz eines vorgelagerten Evaluationsprojekts und begleitender Schulungen wäre das Projekt diesen Umfangs und Neuigkeitsgrades ohne die erfahrenen Partner nicht in der ohnehin knappen Vorgabezeit zu bewältigen gewesen.

Kritisch anzumerken bleibt, dass ein erheblicher Teil der Zeit - typisch für embedded Systeme - in die Entwicklung von Middleware-Schichten geflossen ist. Diese weisen dadurch zwar ebenso wie die Applikation eine klare Struktur auf, dienen aber nicht zur Produktdifferenzierung. Hilfreich wären hier künftig ggf. stärker standardisiere Middlewarepakete, die aber sicher Branchen- bzw. Anwendungstypisch ausgeführt sein müssen.

Literatur- und Quellenverzeichnis

[1] A. Wagener, Ch. Körner, P. Seger, H. Kabza, Anpassung eines Embedded Target für verteilte Steuer- und Regelungsaufgaben an den RTW von Matlab/Simulink, Embedded Intelligence 2001, Nürnberg

[2] U. Lefarth, T. Beck, Qualitäts- und Effizienzsteigerung bei der Entwicklung von Steuergeräte-Software, 3. Stuttgarter Symposium Kraftfahrwesen und Verbrennungsmotoren, 1999, Stuttgart.

[3] Dr. Gary Morgang, AUTOSAR – ein Projekt zur Entwicklung von Steuergeräte-Software, Elektronik automotive 1/2006

 

Beitrag als PDF downloaden

 


Modellierung - MicroConsult Trainings & Coachings

Wollen Sie sich auf den aktuellen Stand der Technik bringen?

Dann informieren Sie sich hier zu Schulungen/ Seminaren/ Trainings/ Workshops und individuellen Coachings von MircoConsult zum Thema Modellierung /Embedded- und Echtzeit-Softwareentwicklung.

 

Training & Coaching zu den weiteren Themen unseren Portfolios finden Sie hier.


Modellierung - Fachwissen

Wertvolles Fachwissen zum Thema Modellierung /Embedded- und Echtzeit-Softwareentwicklung steht hier für Sie zum kostenfreien Download bereit.

Zu den Fachinformationen

 
Fachwissen zu weiteren Themen unseren Portfolios finden Sie hier.