Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller

 
WEITER LESEN
Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller
Entwicklung und Aufbau eines
  automatisierten Testsystems für Peripherie
       verschiedener Mikrocontroller

                            Pius Horn

                     BACHELORARBEIT
  zur Erlangung des akademischen Grades Bachelor of Science (B.Sc.)

            Studiengang Angewandte Informatik

  Fakultät Elektrotechnik, Medizintechnik und Informatik
Hochschule für Technik, Wirtschaft und Medien Offenburg

                           30.03.2021

Durchgeführt bei der Firma querdenker engineering GmbH

                             Betreuer
        Prof. Dr. D. Fischer, Hochschule Offenburg
  M.Sc. Florian Seibold, querdenker engineering GmbH
Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller
Horn, Pius:
Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener
Mikrocontroller / Pius Horn. –
BACHELORARBEIT, Offenburg: Hochschule für Technik, Wirtschaft und Medien Offen-
burg, 2021. 43 Seiten.

Horn, Pius:
Development and construction of an automated test system for peripherals of various mi-
crocontrollers / Pius Horn. –
BACHELOR THESIS, Offenburg: Offenburg University, 2021. 43 pages.
Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller
Eidesstattliche Erklärung
Hiermit versichere ich eidesstattlich, dass die vorliegende Bachelor-Thesis von mir
selbstständig und ohne unerlaubte fremde Hilfe angefertigt worden ist, insbesonde-
re, dass ich alle Stellen, die wörtlich oder annähernd wörtlich oder dem Gedanken
nach aus Veröffentlichungen, unveröffentlichten Unterlagen und Gesprächen ent-
nommen worden sind, als solche an den entsprechenden Stellen innerhalb der Arbeit
durch Zitate kenntlich gemacht habe, wobei in den Zitaten jeweils der Umfang der
entnommenen Originalzitate kenntlich gemacht wurde. Ich bin mir bewusst, dass
eine falsche Versicherung rechtliche Folgen haben wird.

Ich bin damit einverstanden, dass meine Arbeit veröffentlicht wird, d. h. dass die
Arbeit elektronisch gespeichert, in andere Formate konvertiert, auf den Servern der
Hochschule Offenburg öffentlich zugänglich gemacht und über das Internet verbrei-
tet werden darf.

Offenburg, 30.03.2021

Pius Horn
Zusammenfassung

Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie
verschiedener Mikrocontroller

Das Ziel der vorliegenden Abschlussarbeit war es, ein automatisches Testsystem
für die Entwicklung der Embedded-C++-Softwarebibliothek semf zu erstellen. Bei
den Software-Modulen die dabei getestet werden, handelt es sich um sogenannte
Hardware-Abstraktionen, die die Verwendung der von den Mikrocontroller Her-
stellern bereitgestellten Hal-Bibliotheken vereinfachen.
Die wohl größte Herausforderung dieser Abschlussarbeit bestand darin, die von
dem zu testenden Gerät nach außen gesendeten Nachrichten zu validieren, so wie
von außen kommende Nachrichten zu generieren, mit welchem die Leseoperationen
des zu testenden Gerätes überprüft werden können. Mithilfe einer Helper-Gegenstelle
konnte dies jedoch umgesetzt werden. Das Ergebnis ist ein System mit welchem
bestehende und zu entwickelnde Hardware-Abstraktionen zuverlässig, automatisch
und effizient validiert werden können.
Diese Abschlussarbeit ist sowohl für Nutzer von semf, als auch für diejenigen Soft-
wareentwickler interessant, die sich für das Testen von eingebetteten Systemen in-
teressieren.
Abstract

Development and construction of an automated test system for peripherals of
various microcontrollers

The aim of this thesis was to develop an automatic test system for the Development
of the embedded C++ software library semf. The software modules that are tested
are so-called hardware abstractions, which simplify the use of the Hal libraries pro-
vided by the microcontroller manufacturers.
Probably the biggest challenge of this thesis was to validate messages sent to the
outside of the device, as well as to generate messages coming from outside, with
which the read operations of the device to be tested can be checked. However, this
could be implemented with the help of a helper remote station. The result is a sys-
tem with which existing and to be developed Hardware abstractions can be validated
reliably, automatically and efficiently.
This thesis is for users of semf as well as for those software developers who are
interested in testing embedded systems.
Inhaltsverzeichnis

1. Einleitung und Einführung                                                                                1
   1.1. Motivation und Problemstellung . . . . . . . . . . . . . . . . . . .                                2
   1.2. Zielsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                             3
   1.3. Herausforderungen . . . . . . . . . . . . . . . . . . . . . . . . . .                               4

2. Stand der Technik                                                                                        5
   2.1. Embedded Systems Software . . . . . . . . . . . . .                .   .   .   .   .   .   .   .    5
        2.1.1. C++ in Embedded Systems . . . . . . . . . .                 .   .   .   .   .   .   .   .    5
        2.1.2. C vs. C++ Wartbarkeit und Entwicklungszeit                  .   .   .   .   .   .   .   .   10
        2.1.3. Modulare Softwareentwicklung . . . . . . .                  .   .   .   .   .   .   .   .   12
        2.1.4. Portabilität . . . . . . . . . . . . . . . . . .            .   .   .   .   .   .   .   .   12
   2.2. Embedded C++ Library: semf . . . . . . . . . . . .                 .   .   .   .   .   .   .   .   13
        2.2.1. Was ist semf? . . . . . . . . . . . . . . . . .             .   .   .   .   .   .   .   .   13
        2.2.2. Motivation semf . . . . . . . . . . . . . . .               .   .   .   .   .   .   .   .   14
        2.2.3. Komponenten . . . . . . . . . . . . . . . . .               .   .   .   .   .   .   .   .   16
        2.2.4. Hardware-Abstraktion . . . . . . . . . . . .                .   .   .   .   .   .   .   .   18

3. Hauptteil                                                                                               20
   3.1. Ausgangssituation . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   20
   3.2. Konzeption . . . . . . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   21
        3.2.1. Design Gesamtsystem . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   21
        3.2.2. Design Softwarearchitektur . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   24
        3.2.3. Testkonzept . . . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   25
   3.3. Umsetzung . . . . . . . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   28
        3.3.1. Software Device Under Test . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   29
        3.3.2. Interface Helper - Device Under Test        .   .   .   .   .   .   .   .   .   .   .   .   32
        3.3.3. Software Helper . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   37
        3.3.4. Testfälle . . . . . . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   39
        3.3.5. Testauswertung . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   40

4. Ergebnis                                                                                                43
   4.1. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Tabellenverzeichnis

                                        v
Inhaltsverzeichnis

Abbildungsverzeichnis                                                                 i

Quellcodeverzeichnis                                                                 ii

Literatur                                                                           iii

A. Anhang                                                                            v
   A.1. Pinout-Tabelle des Helpers . . . . . . . . . . . . . . . . . . . . . .       v
   A.2. Testfälle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   vi

                                         vi
1.    Einleitung und Einführung

Für die Entwicklung von Softwareprojekten für eingebettete Systeme wird heut-
zutage typischerweise entweder C oder C++ als Programmiersprache gewählt. Da-
bei werden von Projekt zu Projekt immer wieder erneut Softwarekonstrukte be-
nötigt, die die Handhabung von Ringspeichern, das Loggen von Daten in einen
EEPROM-Speicherchip oder die Entprellung von Schaltern ermöglichen. Häufig
bedeutet das für Softwareentwickler, dass für ähnliche Problemstellungen immer
wieder Programmcode-Auszüge aus verschiedenen Quellen wie dem Internet oder
eigenen Softwareprojekten zusammengeführt werden müssen. Sowohl bei C, ins-
besondere aber bei C++ geht dieser Vorgang mit Abstrichen in der resultierenden
Softwarearchitektur einher, da verschiedene Programmcode-Auszüge, bzw. Module
nicht aufeinander abgestimmt sind. Dieser Ansatz unterliegt zudem der Problema-
tik, dass er zeitaufwändig ist.
Das C++-Framework semf (smart embedded framework) von dem Unternehmen
querdenker engineering soll diesen Prozess der Entwicklung von Software für ein-
gebettete Systeme vereinfachen und beschleunigen. Dies wird mit einer Vielzahl
von Softwaremodulen, die von Entwicklern wiederverwendet werden können, er-
möglicht. Diese Softwaremodule von semf lassen sich in hardwareunabhängige
Module und Hardwareabstraktionen unterteilen, wobei sich diese Abschlussarbeit
lediglich mit letzteren beschäftigt. Hardwareabhängige Module basieren auf der
von den Mikrocontrollern bereitgestellten Peripherie und der zugehörigen Hardwa-
re Abstraction Layer (HAL). Die HAL ermöglicht einen abstrahierten Zugriff auf
die Hardware, wie zum Beispiel die Peripherie des Mikrocontrollers. Durch diese
HAL-Schnittstelle kann eine Reduktion der Hardwareabhängigkeit ermöglicht wer-
den, indem sie von dem Mikrocontroller Hersteller einheitlich bereitgestellt werden
[Ro19]. Die von der Hardware abhängigen semf Module, beziehungsweise Hard-
wareabstraktionen, lassen sich als eine Bibliothek verstehen, die sich eine Abstrak-
tionsebene oberhalb dieser HAL befindet (siehe Abbildung 1.1).

                                         1
1. Einleitung und Einführung

                 Abbildung 1.1: Schichtenmodell semf Hardwareabstraktionen

Projekte die mithilfe von semf entwickelt werden sind prinzipiell hardwareunab-
hängig, da für die Peripherie von verschiedenen Mikrocontrollern entsprechende
Implementierungen seitens semf verfügbar sind. Diese semf-Projekte verfügen ty-
pischerweise über eine Programmcode Datei, die die Gesamtheit der Hardware wie-
derspiegelt. Bei einer Hardwareänderung ist lediglich diejenige Datei anzupassen.
Dass sich das Verhalten der Hardware-abhängigen semf Module für jeglichen Mi-
krocontroller gleichermaßen verhält, beziehungsweise dass ihre fehlerfreie Funkti-
on, soll im Rahmen dieser Abschlussarbeit getestet werden.

1.1. Motivation und Problemstellung

Die Hardwareabhängigen Softwaremodule von semf wurden bis dato nicht durch-
gängig getestet. Eine fehlerfreie Funktionalität, insbesondere bei verschiedenen spe-
zielleren Anwendungsfällen, konnte bisher nur mit hohem manuellen Aufwand ge-
währleistet werden. Um die Hardwarekomponenten zu testen, kann nicht auf ein-
fache Unit-Tests zurückgegriffen werden. Dies liegt daran, dass die Tests auf der
tatsächlichen Hardware ausgeführt werden müssen und die Kommunikation mit ei-
ner Gegenstelle (ein weiterer Mikrocontroller) notwendig ist, um einige Peripherien
testen zu können.
Fehlerhaftes Verhalten der semf-Bibliothek kann sowohl auf Bugs in der Mikro-
controller HAL oder auf Bugs in der semf-Bibliothek zurückzuführen sein. Diese
Fehlverhalten sollen mithilfe eines automatisierten Testsystems erfasst werden kön-
nen. Bei diesen Tests handelt es sich um Gray-Box Tests, da die interne Struktur der
zu testenden Module für den Testersteller zwar partiell bekannt ist, die tatsächlichen
Testroutinen jedoch lediglich von den von semf bereitgestellten Schnittstellen ab-
geleitet werden [ST20].

                                            2
1. Einleitung und Einführung

Die Module die auf den folgenden Peripherien basieren, könnten mit Hilfe dieses
Testsystems getestet werden:
    • UART
    • SPI-Master/ SPI-Slave
    • I2C-Master/ I2C-Slave
    • CAN
    • PWM
    • GPIO
    • Input Capture
    • Analog In (mit und ohne DMA)
    • Analog Out (mit und ohne DMA)
    • Flash-Speicher
Aktuell existieren semf Module für diese Peripherien für verschiedene STM32 Mi-
krocontroller und für den Esp32S2 Mikrocontroller. Das Angebot an semf Imple-
mentierungen für weitere Mikrocontroller wird jedoch in naher Zukunft ausgeweitet
werden. Zu diesem Zeitpunkt können demnach lediglich die genannten Mikrocon-
troller getestet werden.
Die zu erstellenden Tests sollen mit möglichst wenig Aufwand und möglichst ohne
Redundanz auf verschiedenen Mikrocontrollern lauffähig sein.

1.2. Zielsetzung

Ziel dieser Abschlussarbeit ist es, sowohl die Software für ein automatisiertes Test-
system zu entwickeln, als auch den tatsächlichen Aufbau der verschiedenen Mi-
krocontrollern mit den Gegenstellen durchzuführen. Des Weiteren sollen möglichst
viele Tests für verschiedene semf Module geschrieben werden, wobei das vollstän-
dige Testen aller Hardwareabhängigen Module den zur Verfügung stehenden zeitli-
chen Umfang dieser Abschlussarbeit überschreiten würde. Die zu erstellenden Tests
sollen mindestens auf zwei verschiedenen Mikrocontrollern (zum Beispiel einem
STM32F7-, einem STM32F3- und einem Esp32S2-Entwicklungsboard) lauffähig
sein. Damit ist sichergestellt, dass die Tests unabhängig von dem Mikrocontroller

                                         3
1. Einleitung und Einführung

funktionieren.

Eine gewünschte Anforderung an die Testroutinen ist, dass diese einfach verständ-
lich und problemlos erweiterbar sind. Zudem soll möglich sein, dass die Tests
für die verschiedenen Mikrocontroller automatisiert durchgeführt werden können.
Hierzu gehört, dass die aktuellste Testsoftware automatisch auf die verschiedenen
Mikrocontroller geflasht und anschließend gestartet wird. Außerdem sollen auf dem
Hostsystem (Skript-Anwendung auf einem Server an welchen die Mikrocontroller
angeschlossen sind) alle Fehlermeldungen zurück gegeben werden können.

1.3. Herausforderungen

Die Umsetzung des automatisierten Testsystems ist mit verschiedenen Herausforde-
rungen verbunden. Hierzu gehört zum Beispiel die Umsetzung der Kommunikation
mit einer Gegenstelle zur Validierung der Peripheriefunktionalität. Dies birgt Kom-
plexitäten wie die Synchronisation zwischen zu testendem Mikrocontroller und Ge-
genstelle, sowie der allgemeinen Entwicklung der Gegenstelle.
Eine weitere Herausforderung besteht darin, dass alle Tests nur einmal geschrieben
werden sollen. Diese sollen auf allen Mikrocontrollern lauffähig sein sollen, die die
entsprechende Peripherie besitzen.
Zudem sollen die Mikrocontroller automatisch geflasht und anschließend die Tests
gestartet und ausgewertet werden.

                                         4
2.    Stand der Technik

Dieses Kapitel stellt die Grundlage für den Hauptteil dieser Abschlussarbeit dar.
Dafür wird zuerst auf die Entwicklung von Embedded Software allgemein einge-
gangen und anschließend auf die Embedded Software Entwicklung mit Zuhilfenah-
me der Embedded-C++-Bibliothek semf. Diese Chronologie ermöglicht, dass die
Motivation und Anwendungsfälle von semf, einleuchtend dargestellt werden. Ziel
dieses Kapitels ist es, dass der/die Leser/in die Abschlussarbeit und deren Nutzen
besser einordnen kann.

2.1. Embedded Systems Software

Zu Beginn der Entwicklung eines jeden Embedded Software Projekts sollte man
sich Gedanken über die zu verwendende Programmiersprache und die Softwarear-
chitektur machen.
In diesem Unterkapitel wird C zu C++ in verschiedenen Gesichtspunkten gegenüber
gestellt und modulare und portable Softwarearchitektur erläutert.

2.1.1. C++ in Embedded Systems

Viele Jahre wurde der überwiegende Anteil an Embedded Projekten mit der Pro-
grammiersprache C umgesetzt [Vi20]. Durch ihre Vielseitigkeit, Kompaktheit und
hohen Performanz des kompilierten Programmcodes etablierte sich C zu der Pro-
grammiersprache, die auf die Anforderungen der Embedded Software Entwick-
lern optimal zugeschnitten ist. Dies ist insbesondere bei überschaubaren und nicht
allzu umfangreichen Projekten auch heute noch der Fall. Mit zunehmender Kom-
plexität von Softwareprojekten werden jedoch die Einschränkungen von C immer

                                        5
2. Stand der Technik

deutlicher. Hierzu gehören insbesondere das fehleranfällige Ressourcenmanage-
ment und die begrenzten Möglichkeiten Abstraktionen auf höherer Ebene mithilfe
von Programmiersprachen-Funktionalitäten umzusetzen. Insbesondere für die Ent-
wicklung von komplexen Applikationen ist somit ein hohes Maß an Zeit und Mühe
notwendig [Vi20].
Zur gleichen Zeit unterliegt die im Jahr 1985 von Bjarne Stroustrup veröffentlichte
Programmiersprache C++ einem ständigen Wandel in Form von hinzukommenden
Features und internen Verbesserungen [St18a]. Diese Verbesserungen werden in
fortlaufend erscheinenden Versionen veröffentlicht und tragen dazu bei, dass sich
C++ als die beste Wahl für komplexe und moderne eingebettete Softwareprojekte
etabliert hat [Vi20]. Die folgenden Features stellen einige Vorteile der Program-
miersprache C++ gegenüber C dar:
    • Eine mächtige und vielseite Standardbibliothek.
    • Objekte und Vererbung.
    • C++ erweitert lediglich C.
    • Threads und zugehörige Speicherverwaltung als Teil der Programmierspra-
      che.

Eine mächtige und vielseitige Standardbibliothek

Die Programmiersprache C++ weist eine funktionsreiche Standardbibliothek auf.
Viele Funktionalitäten die für C Entwickler noch das Aufsuchen von Drittanbie-
ter Bibliotheken erfordern, sind mittlerweile in dieser Standardbibliothek enthalten.
Durch diese Reduktion von externen Abhängigkeiten kann ein stabileres und vor-
hersehbareres Verhalten des entwickelten Programmcodes gewährleistet werden.
Ein weiterer wichtiger Aspekt ist die Wartbarkeit von Programmen: Weil sich nie-
mand in Spezialbibliotheken einarbeiten muss, können standardkonforme Program-
me auch durch anderes Personal mit weniger Aufwand gepflegt werden. Dabei wird
die Kenntnis des Standards vorausgesetzt [Br17].
Der C++ Standard stellt häufig verwendete Datenstrukturen so wie Arrays, binäre
Bäume und Hash-tabellen zur Verfügung. Diese sind allesamt generisch. Ihre Ver-
wendung spart Zeit und resultiert in Applikationen die von Speicherleaks weniger
gefährdet sind, als bei Datenstrukturen die eigens programmiert werden [St18b].

                                         6
2. Stand der Technik

    Darüber hinaus stellt die Standardbibliothek verschiedene Standardalgorithmen wie
    zum Beispiel find, sort, binary search, sowie Permutationen und Set-Operationen
    zur Verfügung. Diese können wiederum auf die zuvor genannten Datenstrukturen
    angewendet werden. Entwickler können sich somit auf diejenigen Aufgaben kon-
    zentrieren, die sich eine Abstraktionsebene oberhalb dieser Algorithmen und Da-
    tenstrukturen befinden.
    Eine weitere Weiterentwicklung gegenüber C, ist die von der C++ Standardbiblio-
    thek bereitgestellte String (std::string) Klasse. Diese weit verbreitete Verwendung
    des String Datencontainers in C++ erlaubt eine intuitivere Art mit Strings umzuge-
    hen als in C. Eine Auseinandersetzung mit Null Terminatoren ist hierbei nicht mehr
    notwendig [Po19].
    In C werden Strings in Form von Char Arrays dargestellt und das String-Ende aus
    Konvention mit einem Null-Byte. Soll das Char-Array dynamisch erstellt werden,
    kann dies mit malloc durchgeführt werden. Dabei muss an das für die Nulltermina-
    tion benötigte Extrabyte gedacht werden. Die Speicherverwaltung liegt dabei in der
    Verantwortung des Entwicklers:
1       char *s1 = strdup("Hallo");
2       char *s2 = malloc(strlen(s1) + 7);
3       strcpy (s2, s1);
4       strcat (s2, ", Welt");
5       free (s1);
6       s1 = s2;

                           Listing 2.1: Dynamische erzeugte Strings in C

    In C++ sind Strings Objekte, die über eine automatische Speicherverwaltung und
    Kontrolle verfügen, wodurch sie insbesondere für Programmierneulinge sicherer
    und einfacher zu verwenden sind:
1       std::string s = "Hello";
2       s += ", Welt";

                                     Listing 2.2: C++ Strings

    Außerdem stehen einige Funktion zur Stringmanipulation zu Verfügung, wie zum
    Beispiel append, find, copy oder replace.

    Objekte und Vererbung

    Programmiersprachen wie C, deren Applikationen als eine Liste aus Instruktionen
    zu verstehen sind, werden prozedurale Sprachen genannt. Diese Folgen an Instruk-

                                                7
2. Stand der Technik

tionen können in Funktionen und Dateien unterteilt werden. Für kleine Programme
werden keine weiteren Organisationsprinzipien (auch Paradigmen genannt [La02])
benötigt: Der Entwickler erzeugt eine Folge von Instruktionen und der Computer
führt diese aus. Je größer und komplexer die Anwendungen werden, desto deut-
licher wird, dass das prozedurale Paradigma konzeptionelle Schwächen aufweist.
Dies lässt sich insbesondere auf zwei Gründe zurückführen [Le19]. Zum einen ha-
ben Funktionen uneingeschränkten Zugriff auf globale Daten. Zweitens, mit Funk-
tionen und Daten die nicht konzeptionell miteinander in Verbindung stehen, lässt
sich nur unbefriedigend ein Modell der Wirklichkeit abbilden. Abbildung 2.1 stellt

    Globale Daten                  Globale Daten                 Globale Daten

    Funktion              Funktion                 Funktion           Funktion

                         Abbildung 2.1: Prozedurales Paradigma

dar, wie in typischen prozeduralen Programmen mehrere Funktionen auf globale
Daten zugreifen und dies zu einer unübersichtlichen Struktur von Zusammenhän-
gen zwischen Funktionen und Daten führt. Dies erschwert außerdem die Wartbar-
keit. Wird eine Änderung an einem globalen Datenobjekt vorgenommen, so müssen
alle Funktionen die auf diese Daten zugreifen angepasst werden.
C++ erweitert C um Klassen aus denen Objekte erzeugt werden können. Diese Ob-
jekte zeichnen sich dadurch aus, dass sie sowohl Daten als auch Funktionalitäten
in Form von Methoden miteinander koppeln, wobei die Objektmethoden mit den
objekteigenen Daten arbeiten. Die Daten eines Objekts lassen sich typischerweise
ausschließlich mit den Objektmethoden lesen und bearbeiten. Die somit erreichte
Datenkapselung (siehe Abbildung 2.2) ist ein wichtiges Prinzip der objektorientier-
ten Sprachen. Sollen Daten eines Objekts modifiziert werden, so ist das lediglich
mithilfe der Methoden dieses Objekts möglich. Dies vereinfacht Schreiben, Debug-
gen und Warten von Programmcode [La02].
Aus Klassen lassen sich außerdem Unterklassen ableiten, wodurch redundanter Pro-
grammcode reduziert wird und die logische Struktur des Projekts verbessert werden

                                          8
2. Stand der Technik

                                        Objekt

                                        Daten
        Objekt                                                         Objekt
                                      Member-
                                     Funktionen
         Daten                                                         Daten

       Member-                                                        Member-
      Funktionen                                                     Funktionen

                       Abbildung 2.2: Objektorientiertes Paradigma

kann. Dieses Konzept wird Vererbung genannt und dient der Wiederverwendbarkeit
von Programmcode.
Auch wenn das objektorientierte Paradigma gegenüber dem prozeduralen Paradig-
ma viele Vorteile verfügt, lassen sich auch verschiedene Nachteile auflisten. Diese
fallen gegenüber den Vorteilen nicht sonderlich ins Gewicht [Ob20]:
    • Erhöhte Kopplung durch Abhängigkeit zwischen Über- und Unterklassen.
    • Leichte Performanzkosten durch Generalisierungen [Go04].
    • Umfangreiche Klassenhierarchien können die Programmgröße des Projekts
      aufblähen.
    • Auf Grund der Vererbung ist es nicht mehr ausreichend die Header-Datei ei-
      nes Moduls zu analysieren, um das Verhalten eine Programmcodedatei zu
      verstehen. Hierzu müssen nun auch die weiteren Headerdateien der Über-
      klassen betrachtet werden. Klassen-Browser können hier jedoch für Abhilfe
      sorgen.

C++ erweitert lediglich C.

Die Programmiersprache C++ wird auch als eine Obermenge von C bezeichnet
[Mi06]. C Programmcode kann somit auch von einem C++-Compiler anstatt ei-
nes C-Compilers übersetzt werden. Daher können zum Beispiel Projekte umgesetzt
werden, die von dem C++-Sprachumfang lediglich die Funktionalität übernimmt

                                           9
2. Stand der Technik

Daten und Funktionen in Klassen, bzw. Objekte zu kapseln, davon abgesehen aber
rein in C programmiert sind [He98]. Ursprüngliche C-Entwickler können daher
verschiedene Sprachfunktionen von C++ übernehmen ohne den vollen Sprachum-
fang auszunutzen. Das ist dann sinnvoll, wenn Entwickler zwar C beherrschen, je-
doch nicht die Zeit aufwenden können und wollen, um den Sprachumfang von C++
ganzheitlich zu erlernen. Zudem können jene Features, welche für den hohen Per-
formanzanspruch von eingebetteten Systemen zu laufzeit- oder speicherineffizient
sind, von den Entwicklern einfach weggelassen werden. Das könnte beispielsweise
Mehrfachvererbung, Templates, Exceptions, Namespaces oder zur Laufzeit durch-
geführte Typerkennungen betreffen [Me14].
Auch wenn C++ lediglich als eine Erweiterung von C anzusehen ist, kann der Um-
stieg von C zu C++ bedeuten, dass Zeit und Ressourcen in Fortbildungen investiert
werden müssen um von den zusätzlichen Features von C++ profitieren zu können.
Insbesondere der Wechsel von dem prozeduralen hin zu dem objektorientierten Pro-
grammierparadigma kann für Entwickler eine größere Umstellung bedeuten.

Threads und zugehörige Speicherverwaltung als Teil der
Programmiersprache.

Der C++11 Standard umfasst eine klar definierte Speicherverwaltung für Projekte
die auf mehreren Threads basieren. Im Gegensatz dazu wurden in C hierfür exter-
ne Bibliotheken wie zum Beispiel pthreads benötigt, um Applikationen mit meh-
reren Threads zu entwickeln zu können. C++-Entwickler können hierzu auf die
zu C++ zugehörige Standardbibliothek zurückgreifen. Zur Verfügung stehen zum
Beispiel Klassen zur Threaderzeugung, Locks und Mutexe, konditionale Variablen
und atomare Variablen. All dies ist auch für Embedded Systems Entwickler inter-
essant. Diese Entwickler können somit Multithreading-Applikationen mit determi-
nistischem Verhalten umzusetzen, die zusätzlich für alle möglichen Zielarchitektu-
ren portierbar sind.

2.1.2. C vs. C++ Wartbarkeit und Entwicklungszeit

Die Entwicklung und Wartung von Software ist teuer. Studien ergaben, dass die
Wartungskosten dabei meistens mindestens 50% und manchmal sogar bis zu 90%

                                       10
2. Stand der Technik

der insgesamten Softwarekosten einnehmen [Gr03]. Außerdem ergab eine Studie
von dem National Institute of Standards an Technology (NIST), dass die US-Wirtschaft
jährlich ungefähr 60 Billionen Euro durch Softwarebugs und den dazugehörigen
Aufwänden und Verlusten einbüßen [02]. Das Department of Computer Science and
Engineering der Universität von Kalifornien, Riverside untersuchte die Auswirkung
der Wahl zwischen C und C++ auf die Programmcodequalität und die Wartbarkeit
[Pa11] und kam hierbei auf interessante Ergebnisse. Untersucht wurden hierbei vier
größere Open-Source-Projekte wie zum Beispiel Firefox und Blender, deren Co-
debasis größtenteils sowohl aus C als auch C++ bestehen. Dabei wurde folgenden
Hypothesen aufgestellt und untersucht:
   • H1: Im Laufe der Zeit verdrängt der C++-Code den C-Code innerhalb eines
     Projekts.
   • H2: C++-Code weist eine höhere interne Qualität als C-Code auf.
   • H3: C++-Code ist weniger fehleranfällig als C-Code.
Für die erste Hypothese (H1) wurden die Projekte über einen Zeitraum von un-
gefähr zehn Jahren auf den Verlauf der verhältnismäßigen Anteile an C und C++
untersucht. Dabei wurde auf das Verhältnis zwischen C- zu C++-Dateien, sowie auf
deren Verhältnis zwischen effektiven Codezeilen (eLOC) von C zu C++ geschaut.
Mithilfe der erhobenen Daten und einer darauf angewendete linearen Regression
konnte festgestellt werden, dass sich bei drei von vier der untersuchten Projekten
das Verhältnis hin zu mehr C++ verschoben hat. Das deutet an, dass in Projekten
die sowohl C- als auch C++-Code enthalten der C-Code tendenziell von C++-Code
verdrängt wird.
Für die Ermittlung der internen Code-Qualität der C- und C++-Programmcodedateien
(H2) wurden diese auf ihre cyclomatic complexity und auf ihre interface complexity
untersucht. Da die Normalisierung der Komplexität durch die Division der vorzu-
findenden eLOC zu einer verzerrten Komplexität führt, wurde statt dem arithmeti-
schen Mittel das geometrische Mittel für die Ermittlung der internen Komplexität
gewählt. Die interne Code-Qualität lässt sich mitunter damit beschreiben, wie ein-
fach der Code zu verstehen ist und ob er einfach zu warten ist. Die Komplexität gibt
hierfür ein gutes Maß. Bei den ermittelten Komplexitäten der C++- und C-Dateien
der vier untersuchten Applikationen konnte eine deutlich höhere Komplexität bei
den C- gegenüber den C++-Dateien festgestellt werden. Dies lässt auf eine tenden-
ziell bessere Code-Qualität bei C++-Dateien schließen.

                                       11
2. Stand der Technik

Für die Bestimmung der Fehleranfälligkeit (H3) wurden alle behobenen Bugs die-
ser vier Applikationen darauf hin analysiert, welche C- und C++-Dateien für die
Behebung der Bugs relevant waren. Die Bugdichte war hierbei bei dem C-Code
entschieden höher als bei dem C++-Code.
Die Studie gibt einige Hinweise darauf, dass die Verwendung von C++ gegenüber
zu C eine höheren Programmcodequalität und bessere Wartbarkeit führt, was sich
wahrscheinlich auch auf Embedded Systems Software übertragen lässt.

2.1.3. Modulare Softwareentwicklung

Die Skalierbarkeit eines Softwareprojekts geht eng mit der Einhaltung des Pro-
grammierprinzips einer modularen Softwarearchitektur [Li07] einher. Unter einer
sogenannten funktionalen Modularität versteht sich die Separierung von Funktio-
nalitäten in strukturelle Einheiten. Somit wird die interne und externe Kopplung
von strukturellen Einheiten möglichst minimiert, wodurch diese Module vielfach
innerhalb eines Projekts wiederverwendet werden können. Beispiele für die Umset-
zung von Modularität sind Verwendungen von Klassen die durch Vererbung wie-
derverwendet werden können, sowie generische Containerklassen und strukturell
gekapselte Funktionalitäten.

2.1.4. Portabilität

Embedded Systems Entwickler in Industriesegmenten wie Automotive-Steuerung,
medizinische Ausrüstung und Verbraucherelektronik stehen unter der Herausfor-
derung Produkte schnell und günstig zu entwickeln. Häufig werden Produkte mit
Aussicht auf ganze Produktfamilien entwickelt. Dies stellt die Entwickler vor die
Herausforderung, die Software auf einen anderen Mikrocontroller oder Geräte mit
sich unterscheidenden Peripherien und Funktionalitäten zu portieren [G 96]. Wie
aufwändig solch eine Portierung ist hängt damit zusammen, wie eng die Softwa-
re an die zugrunde liegende Hardware gekoppelt ist und ist somit eine Frage der
Softwarearchitektur. Ziel soll sein, möglichst viel Programmcode eines Vorgänger-
modells eines Produkts wiederverwenden zu können.

                                       12
2. Stand der Technik

2.2. Embedded C++ Library: semf

Das Ziel dieser Abschlussarbeit ist es, die Embedded-C++-Bibliothek semf bes-
ser testen zu können. Um zu verstehen wie das Testen funktionieren sollen, ist es
wichtig zu wissen, was semf ist und wie semf aufgebaut ist. All dies soll in diesem
Unterkapitel näher beleuchtet werden.

2.2.1. Was ist semf?

semf ist eine Softwarebibliothek für die Embedded C++ Entwicklung. Sie umfasst
eine Vielzahl an Softwarekomponenten, mit welchen wiederkehrende Standardauf-
gaben gelöst werden können und somit Entwicklungszeit gespart werden kann. Die
zur Verfügung gestellten Softwarekomponenten sind typischerweise generisch, so-
dass sie durch die offene Klassenarchitektur einfach angepasst werden können. Ent-
wickelt wird die Bibliothek von dem Unternehmen querdenker engineering welches
sich auf die Entwicklung von Embedded Systemen mit Schwerpunkt Embedded
Software spezialisiert hat.
Selbstgeschriebene Softwarekomponenten im Bereich Embedded können von Ent-
wicklern für weitere Projekte häufig wiederverwendet werden. So wurde von quer-
denker engineering ein Repertoire an Softwaremodulen aufgebaut, auf welches für
interne Projekte zurückgegriffen werden kann. Auf Basis dieses Repertoires kris-
tallisierte sich im Laufe der Zeit das Produkt semf heraus, welches seit 2020 auch
offiziell vermarktet wird. Die Module von semf wurden unter dem Anspruch entwi-
ckelt, dass sie folgende vier Kriterien klar erfüllen müssen [Se20]:
   1. Funktional
   2. Wiederverwendbar
   3. Testbar
   4. Benutzerfreundlich
Durch die Plattformunabhängigkeit ist semf somit für all jene interessant, die ein
Embedded Software Projekt in der Programmiersprache C++ umsetzen möchten.

                                        13
2. Stand der Technik

2.2.2. Motivation semf

Um die Entwicklung von Embedded Software zu vereinfachen, existieren verschie-
dene Programmbibliotheken die für den Entwickler in Frage kommen können. So
gibt es zum Beispiel kommerzielle Produkte von dem Unternehmen Segger oder Qt,
sowie Open-Source-Projekte wie embedOS oder die Software von Arduino. Wie
sich semf von diesen genannten Projekten unterscheidet, wird nachfolgend erläu-
tert. Hierzu werden die verschiedenen Projekte grob zusammengefasst.

Segger

Das Unternehmen Segger ist ein namhafter Hersteller von Software und Programmier-
und Entwicklungstools für eingebettete Systeme. Hierzu gehört unter anderem das
Echtzeitbetriebssystem embOS und verschiedene Middleware Treiber und Module
in den Bereichen Konnektivität, Kryptografie und Sicherheit sowie in IoT, welche
primär in C entwickelt wurden. Ein Interessent für die Software von Segger kann
diese verschiedenen Treiber als jeweils eigenständige Module erwerben. Ein poten-
zieller Nachteil kann sein, dass die verschiedenen Module nicht immer sehr flexibel
sind. Hierzu ein Beispiel:
     Als Käufer interessiere ich mich für einen von Segger entwickelten Bootloader
     mit zugehörigem Updater, was prinzipiell out-of-the-box funktioniert. Dabei
     kann ich mich für einen von mehreren Bootloader-Ausführungen entscheiden.
     Diese unterscheiden sich zum Beispiel in der Übertragungsweise der für die
     Aktualisierung vorgesehenen Firmware. Entscheide ich mich für die Ausfüh-
     rungen mit dem USB-Interface als Übertragungsmedium, so ist der USB-Stack
     fest in den Bootstrap-Loader implementiert. Will ich nun den USB-Stack zu-
     sätzlich für einen anderen Anwendungsfall nutzen, zum Beispiel für das Aus-
     lesen und Ansteuerung des Geräts, so ist das nicht ohne weiteres möglich.
     Hierfür muss die Segger Software manuell angepasst werden. Dies ist wieder-
     um fehleranfällig, da fremder Programmcode überbearbeitet werden muss.
Die Softwaremodule weißen zwar in sich eine hohe Qualität auf, kombinieren sich
untereinander jedoch nur bedingt, was auf die grundlegende Architektur von Segger
zurückzuführen ist.

                                       14
2. Stand der Technik

Qt

Mit dem Toolkit Qt lassen sich grafische Benutzeroberflächen programmieren ,so-
wie plattform- und hardwareunabhängige Projekte in der Programmiersprache C++
entwickeln. Dank der Plattformunabhängigkeit und der Benutzerfreundlichkeit ist
Qt unter Entwicklern weit verbreitet. Qt eignet sich zudem ausgezeichnet für ein-
gebettete Systeme, vorausgesetzt das zu entwickelnde Gerät verfügt über das Be-
triebssystem Embedded Linux. Soll Qt für eingebettete Geräte verwendet werden,
so ergibt sich die Voraussetzung, dass der zugrunde liegende Mikrocontroller um die
entsprechende Rechenleistung und Speicherkapazitäten verfügt. Dies ist notwendig
um Embedded Linux betreiben zu können. Für besonders günstige und durch die
geringe Rechenleistung energieeffiziente Mikrocontroller ist Embedded Qt daher
nicht ausgelegt.

Mbed und Arduino

Die kostenlosen Open Source Bibliotheken von Mbed oder von Arduino stellen
ebenfalls ein Repertoire an verschiedenen Komponenten bereit, welche die Ent-
wicklung von eingebetteten Geräten vereinfachen. Mbed ist für Arm Cortex-M Mi-
krocontroller ausgelegt und wird in Kombination mit dem zugehörigen Betriebs-
system Mbed OS verwendet. Arduino ist ein Unternehmen, dass sowohl Hardware
als auch Software produziert, welche frei verwendet werden können [ar20]. Bei-
de Bibliotheken verfügen über eine sehr aktive Community. Durch diese wird eine
Vielzahl an Beispielprogrammen und Module bereitgestellt, welche für eigene Pro-
jekte herangezogen werden können. Für industrielle, insbesondere für sicherheits-
kritische Anwendungsfälle entspricht eine Community-basierte Bibliothek gegebe-
nenfalls nicht den Ansprüchen in Kriterien wie Qualität, Dokumentation, Support
und Zuverlässigkeit.

semf

Bei der Wahl der optimalen Bibliothek für die Entwicklung von eingebetteten Sys-
temen steht den Entwicklern also eine breite Auswahl an Optionen zur Verfügung.
Die Motivation hinter semf liegt darin, eine Bibliothek bereitzustellen welche ohne
die zuvor genannten Nachteile verwendet werden kann. Die semf-Komponenten,

                                        15
2. Stand der Technik

welche in dem folgenden Unterkapitel näher erläutert werden, sind aufeinander ab-
gestimmt. So ist zum Beispiel die Konfiguration des Bootloaders bezogen auf das
Kommunikationsmedium variabel anpassbar und Kommunikationsprotokolle sind
unkompliziert austauschbar. Semf ist sowohl in bare-metal Projekten, als auch in
Projekten welche über ein Betriebssystem wie zum Beispiel FreeRTOS verfügen,
verwendbar.

2.2.3. Komponenten

Semf verfügt über verschiedene Komponenten, welche sich in verschiedene Kate-
gorien gliedern lassen. Sie sind völlig losgelöst von der Hardware und können somit
in Kombination mit jedem Mikrocontroller verwendet werden.

Core

Die Kernkomponenten sind ein zentraler Bestandteil der Library und vieler semf-
Komponenten selbst. Hierzu gehören zum Beispiel verkettete Listen und Queues,
sowie Ringpuffer Strukturen als auch eine Signal-Slot Funktionalität. Mit Signals
lassen sich in semf, ähnlich wie in Qt, Callbackfunktionen ermöglichen. Ein Signal
kann von dem Programm, wenn es eine entsprechende Bedingung erfährt, aktiviert
werden (zum Beispiel zum Ende eines Interrupts). Ein Slot ist eine normale Funkti-
on oder Methode, welchem ein Signal zugewiesen werden kann. Der zugewiesene
Slot wird aufgerufen, wenn nachfolgend das Signal aktiviert wird. Gegenüber den
gewohnten Herangehensweisen, bietet dies eine intuitivere Art auf externe Ereig-
nisse innerhalb des Programmcodes einzugehen.

Bootloader

Um die Durchführung von Firmwareupdates der eingebetteten Systemen zu ermög-
lichen, stellt semf eine Firmwareupdater- und Bootloaderfunktionalität bereit. Da-
mit lässt sich unkompliziert Konfigurieren, wohin die Firmwareupdates geschrieben
werden sollen, was für ein Verschlüsselungsverfahren verwendet werden soll und
wie groß die zu schreibende Firmware maximal sein darf. Der Übertragungsweg,
sei es zum Beispiel Ethernet, USB oder Uart, kann dann selbst gewählt und konfi-

                                        16
2. Stand der Technik

guriert werden. Für die Verschlüsselung der aktuellen und zu übertragene Firmware
gibt es desktopseitig eine entsprechende Anwendung.

Kommunikation

Semf verfügt über mehrere standardisierte Interfaces für Kommunikationsproto-
kolle wie zum Beispiel Spi, I2c, Can, Uart, Usb und Ethernet. Das ermöglicht
einen einfachen Austausch des Kommunikationsstacks. Abbildung 2.3 gibt einen
schematischen Einblick in die Klassenhierarchie der Communication-Komponente
von semf, am Beispiel von Uart. Das von dem Interface Communication erben-
de Uart-Interface kann unabhängig der tatsächlichen Hardware-Peripherie verwen-
det werden. Die tatsächlichen Hardwarezugriffe passieren erst in den sogenannten
Hardware-Abstrahierungen. Diese unterscheiden sich natürlich je nach Peripherie
und werden in dem Unterkapitel 2.2.4 näher beschrieben. Da alle Interfaces der
verschiedenen Kommunikationsarten von Communication erben und über die glei-
chen Methoden wie write() und read() verfügen, kann der zu verwendende Kom-
munikationsstack einfach ausgetauscht werden. Dies setzt jedoch voraus, das eine
entsprechende Hardware-Abstraktion von semf zur Verfügung gestellt wird oder
alternativ diese selbst implementiert wird.

                                                       Communication
                                           + write()
                                           + read()
                                           …

                                                           Uart
                                           # onDataAvailable()
                                           # onDataWritten()
    Kommunikationsinterface

    Hardware-Abstrahierung                              Stm32Uart
                                           + setFormat()
                                           + setBaud()

                             Stm32F7Uart                                 Stm32F1Uart

                                 Abbildung 2.3: Klassenhierarchie Uart

                                                  17
2. Stand der Technik

Speicherverwaltung

Für verschiedene Speichertechnologien (z.B. Spi-NorFlash, I2c-Eeprom) gibt es
entsprechende semf-Komponenten, sowie ein Logger-Mechanismus und eine EEPROM-
Virtualisierung.

Processing

Zu dieser Kategorie an Modulen gehören verschiedene Algorithmen, die bei der
Verarbeitung von Daten unterstützen. Vom DataStream, der Daten korrekt zwischen
Little- und Big-Endian umrechnet, bis zum LinearInterpolator, der effizient in bis
zu dreidimensionalen Datenquellen interpolieren kann.

System

Semf umfässt Module die sich mit der Systemzeit befassen wie zum Beispiel die
Erstellung von Softwaretimern und Zeitstempeln.

2.2.4. Hardware-Abstraktion

Auch wenn semf konzeptionell architektonisch unabhängig von der Hardware auf-
gebaut ist, gibt es auch Komponenten die grundlegend von der Hardware, wie zum
Beispiel der Peripherie, abhängig sind. In Abbildung 2.3 wurde schon angedeutet,
wie dieser Aspekt in semf umgesetzt wird. Anstelle von Stm32Uart könnte dort
auch genauso gut Esp32s2Uart stehen. Die weitere Unterteilung von Stm32Uart
in Stm32F7Uart und Stm32F1Uart kommt daher zustande, dass sich die Hal und
der Funktionsumfang je nach Mikrocontrollerfamilie teilweise unterscheiden. Da
sich die Hal je nach Mikrocontroller in ihrem Verhalten unterscheiden kann, sollen
genau diese Implementierungen für verschiedene Mikrocontroller im Rahmen die-
ser Abschlussarbeit getestet werden. Aktuell existieren Plug & Play Implementie-
rungen für die folgenden Mikrocontrollerfamilien: Stm32L0, Stm32F1, Stm32F3,
Stm32F4, Stm32F7 und ESP32-S2. Weitere Implementierungen werden aber fol-
gen.

                                       18
2. Stand der Technik

Das Softwarearchitekturdesign von semf mit den Hardware-Abstraktionen ermög-
licht eine in weiten Teilen von der Hardware unabhängige Softwareentwicklung.
Eine Portierung auf einen anderen Mikrocontroller ist mit minimalen Aufwänden
verbunden, vorausgesetzt seitens semf existiert eine zu diesem anderen Mikrocon-
troller passende Hardware-Abstraktion.

                                      19
3.    Hauptteil

3.1. Ausgangssituation

Zu Beginn dieser Abschlussarbeit fand das Testen von den Hardware-Abstraktionen
lediglich manuell statt, wohingegen die hardwareunabhängigen Komponenten mit-
tels klassischer Unit-Tests auf ihre Korrektheit überprüft wurden. In der Vergan-
genheit führte die zum Teil unvollständige Validierung der Hardware-Abstraktionen
des Öfteren zu aufwändigen Suchen nach Fehlern. Zum Beispiel besteht häufig kei-
ne Gewissheit, ob ein Bug denn nun in der Hardware-Abstraktion oder in der sie
verwendenden Applikation zu suchen ist. Ein weiteres Problem besteht darin, dass
die Hal-Bibliotheken verschiedener Mikrocontrollerfamilien in den meisten Fällen
identisch zueinander sind, sich aber in wenigen Aspekten unterscheiden. Dies führt
dazu, dass das manuelle Testen dieser semf-Module jeweils auf verschiedenen Mi-
krocontrollern durchgeführt werden müssten, was sehr zeitaufwändig ist und zu
einem Übersehen von Fehlern führt. Dass das manuelle Testen aufwändig ist, hängt
auch damit zusammen, dass die Validierung dieser Softwaremodule häufig nicht tri-
vial ist, da sie auf externe Ereignisse, wie zum Beispiel einkommende Nachrichten,
angewiesen sind.
Die Entwicklung und der Aufbau eines automatisierten Testsystems für Periphe-
rie verschiedener Mikrocontroller wurde bis zu diesem Zeitpunkt noch nicht an-
gegangen, da das Repertoire an verschiedener Hardware-Abstrahierungen gerade
erst zu wachsen begann. Da nun immer mehr Mikrocontrollerfamilien unterstützt
werden sollen, stieg die Notwendigkeit dieses automatisierten Testsystems. Zudem
lässt sich die Umsetzung weiterer Module für verschiedene Mikrocontroller mithil-
fe eines automatischen Testsystems beschleunigen. Dies hängt unter anderem damit
zusammen, dass den Entwicklern direkt ersichtlich wird, wenn etwas nicht wie ge-
wünscht funktioniert.

                                       20
3. Hauptteil

Für diese Abschlussarbeit bestanden keine weiteren schon bestehenden Grundlagen
bezüglich des Testsystems.

3.2. Konzeption

Bevor die Umsetzung im Detail erläutert wird, wird in diesem Kapitel auf die Kon-
zeption des Gesamtsystems, die Softwarearchitektur und auf das Testkonzept ein-
gegangen.

3.2.1. Design Gesamtsystem

Für das Design des Gesamtsystems gibt es verschiedene mögliche Optionen, die es
abzuwägen gilt, um die bestehenden Anforderungen an das Testsystem optimal zu
erfüllen. So sollen die Tests zuverlässig, das Testen flexibel und die Testdurchfüh-
rung unkompliziert sein und in die Continuous Integration integriert werden kön-
nen. Aus diesen Anforderungen ergab sich das in Abbildung 3.1 aufgezeigte Design
des Gesamtsystems, dessen Umsetzung möglichst im Rahmen dieser Abschlussar-
beit durchgeführt werden soll.

                                        DUT                         Helper
            Testserver             Mikrocontroller              Mikrocontroller

                                       Stm32F7

     CI-Server   Test-Server           Stm32F4

                                       Esp32S2

                           Abbildung 3.1: Design Gesamtsystem

Nachfolgend werden die verschiedenen Instanzen einzeln erläutert:

                                          21
3. Hauptteil

Tests starten
          Die Tests sollen flexibel durchgeführt werden können. Damit ist gemeint,
          dass die Tests zu den Hardware-Abstrahierungen von semf auch einzeln ge-
          startet werden können oder wahlweise auch nur einzelne Mikrocontroller
          getestet werden können. Das Unternehmen querdenker engineering verfügt
          über einen CI-Server, in welchen das automatische Durchführen der Tests in-
          tegriert werden soll. Hier könnte ein weiterer Computer (zum Beispiel ein
          Raspberry Pi 1 )als Bridge zu den zu testenden Mikrocontrollern dienen. An
          diesem Test-Server werden dann die zu testenden MCUs angeschlossen und
          die Tests gesteuert und ausgewertet. Der Kommunikationskanal zwischen dem
          Test-Server und den zu testenden Mikrocontroller sollte so gewählt werden,
          dass möglichst wenig Datenleitungen benötigt werden, um die Übersichtlich-
          keit des Aufbaus nicht zu gefährden.
Zu testende Mikrocontroller
          Für jeden von semf unterstützten Mikrocontroller wird ein entsprechendes
          Entwicklungsboard, welches über möglichst viele Peripherien verfügt, ausge-
          wählt, und mit dem Test-Server verbunden. Auf die zu testenden Mikrocon-
          troller (Device Under Test bzw. DUT) wird eine Software geladen, welche
          die Tests durchführt.
Helper Mikrocontroller
          Einige Tests benötigen für die Validierung eine Kommunikation mit einem
          Helper Mikrocontroller. Dieser ist dafür zuständig, die von dem DUT an den
          entsprechenden Peripherien geschriebenen Daten zu lesen und somit zu va-
          lidieren. Der Helper soll außerdem verschiedenste Datenströme an verschie-
          denen Peripherien generieren, die wiederum von dem DUT gelesen werden
          können. So können die Lesefunktionalitäten seitens des DUT validiert wer-
          den.
Die möglichen Alternativen und Abwägungen zu diesem Design werden im Fol-
genden genauer erörtert:
Braucht es den Helper?
          Da die Entwicklungsboards über mehrere Schnittstellen für die jeweiligen Pe-
          ripherien verfügen, lassen sich diese Peripherien auch ohne Helper testen. So
          könnte sich wie in Abbildung 3.2 schematisch angedeutet, für jedes Kom-

 1
     Ein Raspberry Pi ist ein Einplatinencomputer mit verschiedenen Schnittstellen

                                                       22
3. Hauptteil

munikationsprotokoll die lesende und schreibende Komponente auf dem sel-
bigen Entwicklungsboard befinden. Letztendlich wurde sich jedoch für die

                Abbildung 3.2: Alternativer Ansatz ohne Helper

Variante in Abbildung 3.3 entschieden. Diese resultiert in einem Design mit

                Abbildung 3.3: Tatsächlicher Ansatz mit Helper

einer deutlich höheren Komplexität, bei der die Kommunikation und Syn-
chronisation mit dem Helper Device, sowie die allgemeine Logik des Helpers
zusätzlich zu den Tests seitens des DUT umgesetzt werden müssen. Auch er-
schwert dies das Debuggen, da potenzielle Bugs nun sowohl auf der DUT
Instanz, als auch auf der Helper Instanz sein können.

Die Entscheidung hin zum dem Ansatz mit einer Helper Gegenstelle bringt
einen großen Vorteil mit sich: Man läuft nicht mehr in die Gefahr, dass man
durch doppeltes Fehlverhalten seitens des DUT fälschlicher Weise auf ein po-
sitives Verhalten rückschließen könnte. Diese potenzielle Fehlerquelle wird
zumindest dadurch minimiert, dass von einer wahrscheinlichen Fehlerfreiheit
des Helpers ausgegangen werden kann. Diese Annahme kann getroffen wer-
den, da jeder auf dem DUT durchgeführte Test auch gleichzeitig als Neben-
produkt den Helper validiert. Außerdem erleichtert der Helper die Entwick-

                                     23
3. Hauptteil

     lung von neuen Treibern, da damit von Anfang an gegen eine funktionierende
     Gegenstelle getestet werden kann.
Braucht es mehrere Helper?
     Abbildung 3.1 zeigt das Design des Gesamtsystems, wobei mit jedem DUT
     ein zugehöriges Helper Device verbunden wird. Denkbar wäre auch gewe-
     sen, alle DUTs mit jeweils der selben Helper Instanz zu verbinden. Durch
     die Designentscheidung hin zu einem jeweiligen Helper pro DUT ist man
     deutlich flexibler. Einzelne DUTs mit ihren jeweiligen Helpern können von
     dem Test-Server abgekoppelt werden und an einem Desktop-PC gedebuggt
     werden, ohne den Aufbau des Gesamtsystems zu stören.

3.2.2. Design Softwarearchitektur

Das Design der Softwarearchitektur des Testsystems lässt sich wie in Abbildung
3.4 in drei Pakete, beziehungsweise Projekte unterteilen. Diese sind DUT, Helper-
Interface und Helper.

                   Abbildung 3.4: Paketdiagramm der Softwarearchitektur

Helper-Interface
     Das Projekt Helper-Interface wird sowohl von den DUT Projekten, als auch
     von dem Helper Projekt importiert. Die Steuerung des Helpers durch das

                                           24
3. Hauptteil

      DUT wird mittels dieses von beiden Instanzen benutzten Interfaces umge-
      setzt. Das Helper-Interface umfasst die beiden Pakete Protocol und Configu-
      rations. Protocol definiert, wie die Nachrichten aufgebaut sind und Configu-
      rations bildet die Einstellungen ab, mit denen die verschiedenen Peripherien
      konfiguriert werden können. Eine mögliche Configuration für GPIO könnte
      zum Beispiel sein: 1. INPUT 2. PULLUP 3. SET_GPIO.
      Dem Helper kann somit mitgeteilt werden, wie er das entsprechende GPIO
      zu konfigurieren hat.
DUT
      Die DUT Pakete stehen für die zu testenden Mikrocontroller. Da jeder Mikro-
      controller eine andere Hal-Bibliothek hat und sich die Peripherien unterschei-
      den können, wird für jeden Mikrocontroller ein eigenes Projekt angelegt. Die-
      se unterscheiden sich aber lediglich in der Konfiguration der Hardware und
      der Hal. Das Abarbeiten der Tests wird mittels Objekten vom Typ Testcon-
      troller geregelt.
Helper
      Der Helper importiert wie die DUTs das Helper-Interface und führt die An-
      weisung der DUT-Seite entsprechend durch.

3.2.3. Testkonzept

Sollen Tests für Software-Module auf einem eingebetteten System umgesetzt wer-
den, so stehen den Entwicklern verschiedene Frameworks zur Verfügung. Mögliche
Frameworks sind zum Beispiel GoogleTest oder uCUnit. Diese wurden schon vor
dieser Abschlussarbeit von den Entwicklern von semf für ähnliche Anwendungs-
fälle ausprobiert und validiert. Als größte Schwäche stellte sich dabei heraus, dass
Asynchronität von den Testframeworks nicht abgebildet werden konnte. Semf ba-
siert jedoch mittels des Signal-Slot Moduls stark auf Asynchronität. Dies gilt insbe-
sondere auch für die Kommunikation zwischen dem Helper und dem DUT. Daher
wurde sich für den Anwendungsfall des Hardwaretesters gegen eine Verwendung
von einem Testframework entschieden. Jene Aufgaben, die ein Testframework ver-
einfachen würden, sind demnach selbst umzusetzen. Hierzu einige Beispiele:
   1. Es gibt keine Makros wie TEST_ASSERT oder TEST_ASSERT_EQUAL.
   2. Die Funktionalität von setUp und tearDown ist selbst zu implementieren.

                                         25
3. Hauptteil

      3. Das Auswerten der Testergebnisse muss selbst übernommen werden.
Die Testdurchführung orientiert sich dabei an XUnit Testpattern [Me07]. Jeder Test
durchläuft somit die in Abbildung 3.5 dargestellten Testphasen.

                                    Abbildung 3.5: XUnit Testphasen

Die Peripherien unterschiedlicher Mikrocontroller unterscheiden sich teilweise in
ihrem Funktionsumfang, wodurch nicht jeder Tests auf allen Mikrocontrollern lauf-
fähig sein wird. Falls möglich, sollen die zu schreibenden Testroutinen jedoch mit
verschiedensten Mikrocontrollern kompatibel sein.
Die XUnit Testpatterns für automatisierte Tests raten an, verschiedene Testprinzipi-
en einzuhalten. Teil des Testkonzepts dieser Abschlussarbeit ist es, diese Testprin-
zipien möglichst umzusetzen. Dazu gehört zum Beispiel die Folgenden:
Minimize Test Overlap
          Die Tests sollten so strukturiert sein, dass möglichst wenige Tests von einer
          bestimmten Funktionalität abhängig sind, da es wenig hilft, wenn diese Funk-
          tionalitäten mehrfach validiert werden.
Verify One Condition per Test
          Häufig erscheint es effizient und verführerisch, einen einzelnen Test zum Tes-
          ten verschiedener Konditionen mehrfach zu verwenden. Somit könnte man
          sich das mehrfache Erzeugen eines Startzustandes ersparen. Diese Herange-
          hensweise sollte vermieden werden, da sie zum Beispiel das Lokalisieren von
          Fehlern erschwert.
Communicate Intent
          Damit sich diese Tests auch in Zukunft einfach pflegen lassen, sollten diese
          klar und verständlich sein. Dies wird mit doxygen-Kommentaren 2 und ei-
 2
     Doxygen ist ein Programm, welches Kommentare aus dem Programmcode extrahiert und daraus eine Doku-
      mentation generiert

                                                   26
3. Hauptteil

     ner einheitlichen Namenskonvention für Testroutinen umgesetzt. Für die Na-
     menskonvention der Tests wurde sich auf die Folgene Darstellung geeinigt:
     module_functionlity_condition_expectedBehavior.
Write the Tests First
     Test-Driven-Development bedeutet, dass zuerst die Tests, und anschließend
     die Implementierungen entwickelt werden [Be02]. Da die Tests für alle Hardware-
     Abstrahierungen wiederverwendet werden können, ist dies in unserem Fall
     automatisch geben.
     Der Ablauf der Entwicklung einer neuen Hardware-Abstraktion soll den in
     Abbildung 3.6 gezeigten Prozess durchlaufen. Das Pushen eines überarbei-

           Abbildung 3.6: Flussdiagramm: Entwicklung einer Hardware-Abstraktion

     teten Softwarestands von Hardware-Abstraktionen aktiviert serverseitig die
     Durchführung der Tests. Falls dabei Tests fehlschlagen, wird dies dem Ent-
     wickler mitgeteilt, so dass dies korrigiert werden kann. Die Tests können aber
     auch lokal an dem Arbeitsplatz durchgeführt werden.

                                           27
3. Hauptteil

3.3. Umsetzung

Wie die in dem vorherigen Kapitel beschriebene Konzeption in die Tat umgesetzt
wurde, wird in diesem Kapitel behandelt.
Aktuell werden sowohl der Mikrocontroller Stm32F3, als auch der Mikrocontrol-
ler Stm32F7 mittels des Testsystems getestet. In Abbildung 3.7 ist zu sehen wie der
Testaufbau des Stm32F3 Mikrocontrollers aufgebaut ist. Die Peripherien der Instan-

              Abbildung 3.7: Aktueller Testaufbau des Stm32F3-Mikrocontrollers

zen Helper und DUT wurden dabei mit zurechtgeschnittenen Überbrückungskabeln
verbunden. Auf die Software der abgebildeten Geräten wird nachfolgend eingegan-
gen. Das Kapitel Umsetzung ist somit in die Beschreibungen der verschiedenen
Softwareprojekte unterteilt, aus welchen sich die Programme des Helpers und der
DUTs zusammensetzen. Die sich daraus ergebenden Unterkapitel lauten:
1. Software Device Under Test 2. Interface Helper - Device Under Test 3. Software
Helper 4. Testfälle
Als visuelle Unterstützung für die Erklärungen der Softwarearchitektur werden des
Öfteren UML-Diagramme verwendet. Diese sind als Modell zu verstehen und stel-
len verschiedene Details zum Teil nur verkürzt dar. Ziel der UML-Diagramme soll
sein, die Gesamtheit der Systeme möglichst verständlich wiederzugeben.

                                            28
Sie können auch lesen