Wiederverwendung, Architekturzentrierung, komponentenbasierte Entwicklung, Pattern, Templates, Muster… All diese Schlagworte spielen, glaubt man der Literatur, offensichtlich eine wichtige Rolle. Nur wenn man dann versucht konkret zu werden, kommt man schnell beim Observer oder einer abstrakten Fabrik an. Muster können aber auch schon viel einfacherer Natur sein. Falls Sie bereits etwas erfahrener in der Programmierung von Mikrocontrollern sind, haben Sie ein weit verbreitetes Pattern bereits oft angewendet. Es ist die MAINLOOP. So einfach können Entwurfsmuster auch daherkommen, der Fachmann nennt solche einfachen Muster auf niedrigem Abstraktionsniveau übrigens Idiom.

Merken wir uns zunächst folgendes: Es geht um Wiederverwendung. Dabei sollen Verhalten oder/und Strukturen wiederverwendet werden. Einen wichtigen Wiederverwendungsmechanismus haben wir bereits kennengelernt und mehr oder weniger bewusst angewendet. Es ist die Vererbung (Generalisierung, Spezialisierung). Weitere Mechanismen sind zum Beispiel die Realisierung von Schnittstellen (Interfaces) oder generischer Klassen (Templates).

Das hier vorgestellte Konzept soll Ihnen eine praktische Anwendung dieser Konzepte ermöglichen. Zwar wurden bereits Tempos vereinzelt angewendet, jedoch soll im weiteren Verlauf des Tutorials dieses Konzept entsprechend konsequenter angewendet werden. Tempos ist eine spezielle Möglichkeit in SiSy einfache Struktur- und Verhaltensmuster wiederzuverwenden.

Es ist eine Mikrocontrolleranwendung zu entwickeln bei der mit einem Taster ein Speaker angesteuert wird. Dabei sollen Taster und Speaker über optische Rückmeldungen verfügen.

Im ersten Schritt ist die Lösung für einen „beleuchteten Taster“ zu entwickeln. Das optische Feedback soll über eine zugeordnete LED realisiert werden. Diese hat die Verarbeitung verschiedener Tasteraktionen durch kurzes Aufblitzen anzuzeigen. Zunächst sollen die Aktionen Klicken und Halten als erkannt optisch zurückgemeldet werden.

Im zweiten Schritt ist der Speaker zu realisieren. Diesem ist eine LED zuzuordnen welche solange blinkt, wie der Speaker aktiv ist. Durch das Klicken des Tasters soll der Speaker aktiviert werden. Das Halten setzt den Speaker wieder zurück.

Falls Sie jetzt noch das Klassendiagramm geöffnet haben, wählen Sie im Kontextmenü (rechte Maustaste) des Diagramms den Menüpunkt nach oben. Sollte das Projekt nicht mehr geöffnet sein öffnen sie das SiSy UML-Projekt wieder. Legen Sie ein neues Klassendiagramm an und wählen Sie die Sprache ARM C++. Beachten Sie die Einstellungen für die Zielplattform STM32F4-Discovery. Beim Öffnen des Diagramms (rechte Maustaste, nach unten) laden Sie aus dem SiSy LibStore die Diagrammvorlage Application Grundgerüst für PEC Anwendungen (XMC, STM32, AVR). Weisen Sie das Treiberpaket für STM32F4 zu.

Der Taster ist wie gehabt an Port A0 angeschlossen. Als Tasterbeleuchtung wollen wir die an Port D15 angeschlossene LED zuordnen. Wir wissen aus vorhergehenden Übungen, dass es nette Lösungen für den Taster und die LED in den Paketen des Framwork zu finden gibt. Bei genauem Hinsehen bemerken wir, dass es sich bei den dargestellten Lösungen für PecButtonClickAndHold, PecLed nicht um Basisklassen im eigentlichen Sinne handelt, sondern um Templates.

Wir können mit dem was wir bisher über die UML wissen bereits ablesen, dass der PecButtonClickAndHold sowie die PecLed über eine Reihe von wichtigen Operationen verfügen und über alle Eigenschaften eines PecAppModul und des PecPinInput bzw. PecPinOutput. Besonders beim PecButtonClickAndHold fällt auf, dass es Operationen gibt die öffentlich (public) sind.

+isPressd()
+waitForPress()

Und es gibt Operationen, die geschützt (protected) sind und demzufolge nicht von außen sondern nur intern in der Klasse oder deren Ableitungen benutzt werden können. Ein weiterer auffälliger Umstand ist, dass diese Operationen auch noch kursiv gestellt sind. Das bedeutet in diesem Fall, dass diese Operationen virtueller Natur sind und überschrieben werden können. Sie sind vom Anwendungsentwickler in einer eigenen Realisierung des PecButtonClickAndHold zu überschreiben. Damit werden dann die Eventhandler für die Ereignisse „Taste klicken“ und „Taste halten“ implementiert.

#onClick()
#onHoldStart()
#onHolding()
#onHoldEnd()

Der PecButtonClickAndHold weist neben dem Sterotyp «template» und den aufgeführten Merkmalen noch eine weitere Besonderheit auf. Er verfügt über ein Attribut mit dem merkmal {sm}. Folgen wir diesem Zustandsattribut „nach unten“ finden wir das Verhaltensmodell des Buttons als Zustandsmaschine.

Damit haben wir uns einen kurzen Überblick zu den vorhandenen Lösungen für Taster und LED verschafft. Die nächste Überlegung zielt auf die Struktur der Komponente „Taster mit Beleuchtung“. Zunächst geben wir dem Ding einen Namen und legen für den Button eine entsprechende Klasse an: LightedButton. Er hat offensichtlich eine eigene (blaue) LED. Diesen Umstand würdigen wir mit der Aggregation (Ganz-Teil-Beziehung) einer LED in der Buttonklasse; ändern Sie dabei manuell auf #led.

Zur schnellen Suche und zum Einfügen der Templates können Sie im Navigator auf UML-Klassen wechseln. Um die vorhandenen Lösungen für unsere neue Komponente wiederzuverwenden ziehen wir den PecButtonClickAndHold sowie die PecLed in das Diagramm und verknüpfen diese mit den Bausteinen unserer Komponente. Als Verbindungstyp wählen wir die Realisierung. Die Zuordnung der konkreten Port-Pins (pinD15 und pinA0) erfolgt ebenfalls über Templates die wir im UML-Ordner Pec/PinList finden.

Die Operationen onClick und onHoldStart fügen wir aus der Objektbibliothek in die Klasse ein. Dabei bietet uns das Werkzeug die vorhandenen virtuellen Operationen zum Überschreiben an. Es lohnt sich jetzt schon mal den Code für diese Komponente zu generieren und anzuschauen. Das Generieren des Codes erfolgt über das Aktionsmenü in der Objektbibliothek (nur Erstellen). Um den Quellcode einzusehen selektieren Sie die Klasse LightedButton und betätigen die Taste F3.

class LightedButton : public PecAppModul
// implements ButtonClickAndHold, pinA00
{
	protected: port_t port;  // Attribut from Template PinInput
	protected: LedBlue led; // Aggregation from Model
	protected: uint8_t volatile holdCounter; // Attribut
	protected: uint8_t volatile releaseCounter; // Attribut
	protected: state_t state; // Attribut for State Machine
	enum{	state_state_Nothing=1,
		state_state_down,
		state_state_click,
		state_state_hold  }; // States for State Machine
 
	public: bool isPressed(); // Operation from Template ButtonClickAndHolde
	public: void waitForPress(); // Operation from Template
	public: virtual void config(...); // Operation from Template PinInput
	public: virtual bool getState(); // Operation from Template PinInput
	public: void changeState_state(state_t newState);  // Operation from State Machine
	public: LightedButton(); //Konstruktor
	public: ~LightedButton(); //Destruktor
 
	protected: void onClick(); // Operation from Template ButtonClickAndHolde 
	protected: void onHoldStart(); // Operation from Template ButtonClickAndHolde
	protected: virtual void onTimer10ms(); // overwritten Operation from Base Class AppModul
	protected: virtual void onHoldEnd(); // Operation from Template ButtonClickAndHolde
	protected: virtual void onHolding(); // Operation from Template ButtonClickAndHolde
};

Der hier gezeigte Quellcode ist etwas kompakter als der vom Codegenerator erstellte Code und zeigt auch nur die Headerdatei der generierten Klasse, aber es lässt sich an dieser bereits erkennen, wie der Codegenerator mit den einzelnen Modellfestlegungen umgegangen ist. Die Generalisierung des Templates zum PecAppModul wurde als Vererbung umgesetzt. Alle als Realisierung ausgeführten Beziehungen haben bewirkt, dass die entsprechenden Struktur- und Verhaltensmerkmale nicht über eine Vererbung sondern durch „Hineinkopieren“ der neuen Komponente zugeordnet wurden.

Das Board STM32F4 weist noch eine Besonderheit auf: es müssen die Realisierungsparameter definiert werden. Markieren Sie dafür die Klasse LightedButton und öffnen Sie mit der rechten Maustaste das Kontextmenü; wählen Sie Definieren. Aktivieren Sie die Schaltfläche Realisierungsparameter. In dem sich öffnenden Dialogfenster wählen Sie für %pinLogic% den Wert High-Aktiv und für %pinPull% den Wert NoPull. Jetzt fehlt noch die Zuordnung zwischen Controller und LightedButton mittels Aggregation und wir erhalten das Klassenmodell der ersten Ausbaustufe.

Klassenmodell der Anwendung in der ersten Ausbaustufe:

Die Applikation selbst bleibt bei dieser Art der Programmierung etwas im Hintergrund. Sie dient als Rahmen und Moderator für die in ihr agierenden aktiven Klassen. Füllen wir jetzt also die Operationen onClick und onHoldStart mit dem erforderlichen Code.

LightedButton::onClick::

led.flash();

LightedButton::onHoldStart:

led.flash();

Übersetzen Sie das Programm. Korrigieren Sie ggf. Schreibfehler. Übertragen Sie das lauffähige Programm in den Programmspeicher des Controllers.

  1. Erstellen (Kompiliern und Linken)
  2. Brennen

Die zugeordnete LED blitzt jetzt kurz auf, wenn ein Klick- oder ein Halten-Ereignis festgestellt wurde.

Im folgenden zweiten Schritt ist der Speaker zu realisieren. Diesem ist eine LED zuzuordnen, welche solange blinkt, wie der Speaker aktiv ist. Durch das Klicken des Tasters soll der Speaker aktiviert werden. Das Halten setzt den Speaker wieder zurück.

Eine vorgefertigte einfache Lösung für den Speaker ist in der hier verwendeten Bibliothek nicht enthalten. Somit setzen wir diese Komponente aus den verfügbaren Bausteinen zusammen und ergänzen die fehlende Funktionalität, einen beliebigen Pin mit einer bestimmten Frequenz umzuschalten (toggeln). Wenn dieser Baustein gut funktioniert können wir den so erstellten einfachen Speaker als Lösungsmuster (Template) zum Beispiel in unsere Bibliothek aufnehmen. Dazu legen wir uns ein Paket „eigene Muster“ an. Achten sie darauf, dass die Option „fast UML“ deaktiviert ist. Öffnen Sie das Paket. Laden Sie keine Vorlage aus dem SiSy LibStore und führen Sie die nachfolgenden Operationen aus.

Der einfache Speaker ist ein Ausgabe-Pin und der Einfachheit halber ein PecAppModul. Wir können dann das 10 Millisekunden Ereignis zum Umschalten des Pins benutzen. Der Speaker soll mit der Operation start aktiviert werden und mit der Operation stop wieder angehalten werden. Das Ereignis onTimer10ms des PecAppModuls überschreiben wir.

Legen Sie ein Klasse SimpleSpeaker an, fügen Sie in diese Klasse ein Attribut isActive ein sowie die Operationen onTimer, start und stop. In unserem Paket eigene_Muster benötigen wir noch die Klassen PecAppModul und PecPinOutput, die wir über den Explorer finden können. Verbinden Sie die Klasse SimpleSpeaker und PecAppModul als Vererbung und mit PecPinOutput als Realisierung.

Schalten Sie jetzt die Klasse SimpleSpeaker im Dialog „Definieren“ auf „Template“ um.

Erstellen Sie die Klasse wie dargestellt. Die Operationen sind wie folgt zu ergänzen:

SimpleSpeaker::start:

isActive=true;

SimpleSpeaker::stop:

isActive=false;

SimpleSpeaker::onTimer10ms:

if (isActive)
   this->toggle();
else
   this->off();

Das von uns vorbereitete Muster können wir jetzt in unserer Anwendung benutzen. Sie finden dies genau wie alle anderen Templates über den Navigator „UML-Pakete“ unter dem Paket eigene_Muster. Vervollständigen Sie das Klassendiagramm entsprechend der Abbildung.

Ergänzen Sie die Methoden wie folgt:

LightedButton::onClick:

led.flash();
app.speaker.start();

LightedButton::onHoldStart:

led.flash();
app.speaker.stop();

Übersetzen Sie das Programm. Korrigieren Sie ggf. Schreibfehler. Übertragen Sie das lauffähige Programm in den Programmspeicher des Controllers.

  1. Erstellen (Kompiliern und Linken)
  2. Brennen

Gratulation! Sie haben ihr erstes eigenes UML-Template erstellt. Verbinden Sie den Speaker auf dem Zusatzboard mit dem Pin PE5 auf dem STM32F4-Discovery.

Sie können jetzt per Click den Speaker einschalten, dabei blinkt die rote LED; mit langem Drücken des Tasters schalten Sie den Speaker wieder ab und die LED aus.

Erlernte und gefestigte Arbeitsschritte:

  1. Klassendiagramm anlegen und öffnen
  2. Diagrammvorlage für PEC Applikation auswählen, laden und Treiberpaket für STM32F4 einfügen
  3. Navigator auf UML Pakete umschalten
  4. gewünschte Klasse im Navigator/Explorer suchen und ins Diagramm ziehen
  5. Klassen aggregieren
  6. eine eigene Klasse konstruieren
  7. Templates anlegen und Realisierungsparameter festlegen
  8. Operationen anlegen und in eine Klasse/Template einfügen
  9. Operationen einer Basisklasse überschreiben
  10. Attribute einer Klasse anlegen
  11. Klassen und Templates zu Komponenten zusammenbauen
  12. den nötigen Quellcode in den Operationen erstellen
  13. Erstellen und Brennen einer ARM Applikation im Klassendiagramm

Und hier diesen Abschnitt wiederum als Videozusammenfassung.

Erweitern Sie die Anwendung selbständig so dass die rote LED des Speakers solange leuchtet wie der Speaker aktiv ist.