Dies ist eine alte Version des Dokuments!


Vorgaben für die Softwareentwicklung

  • Es empfiehlt sich für alle definierten und deklarierten Namen die Englische Sprache zu verwenden. Für Variablen, Funktionen und Kommentare darf die Deutsche Sprache genutzt werden.
  • Eine detailliertere Liste ist im Embedded System development Coding Reference Guide zu finden.
  • Neben der Struktur beim Programmieren zählt auch eine interessante und schöne Umsetzung für den Nutzer in die Bewertung.
  • Stellen Sie Ihrem Programm einen beschreibenden Kommentar voran.
  • Dieser sollte in der Form sein, wie die Beschreibung in den Übungsprogrammen.
  • Beschreiben Sie darin, ob
    • weitere c- oder h-files eingebunden müssen
    • Jumper gesetzt / geöffnet werden müssen
    • spezielle Hardware genutzt werden muss

Beschreibung

  • Während der Entwicklungsphase kann es sich anbieten Code testweise auszukommentieren. Für die finale Version sollten die Kommentare aber „sauber“ sein.
  • Falls es alternative Werte gibt, welche optional sinnvoll sind, können diese und deren Konsequenzen in ein Kommentar gepackt werden.

Beispiel für auskommentierten Code

  • Nutzen Sie für die Manipulation von Bits die vorgegebenen Makros.
  • Beim Erstellen von eigenen Makros sollte auf Querwirkungen geachtet werden, da ein Makro eine Codeersetzung vor dem Compiler durchführt.

Beispiel für die vordefinierten Makros

  • Konstanten per #define sollten z.B. für die feste Größe von Arrays verwendet werden. Sie können (bzw. werden) auch für hardwarenahe Werte, wie Portnummern, genutzt werden.
  • Auch Werte in enum sind Konstanten. Für Konstanten mit ähnlichem Hintergrund (z.B. Tage, s.u.) sollten enum genutzt werden. Damit können Variablen auch als enum-Typ definiert werden, was den Code leserlicher macht.
  • Bei Defines wird keine Typisierung überwacht. Ist dies notwendig empfiehlt es sich const Variablen zu nutzen.
  • Konstanten per #define oder enum sind komplett in Großbuchstaben zu schreiben, um diese von Variablen zu unterscheiden.
  • Falls Sie aus mehreren Wörtern zusammengefügt sind, sollten Sie mit Unterstrich getrennt werden.

enum tage {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG}; 

for (enum tage aktuellerTag = MONTAG; aktuellerTag <= SONNTAG; aktuellerTag++)
{...};

Beispiel für Konstanten

  • Wenn sich Werte im Programm zur Laufzeit ändern, so sollten diese als Variable angelegt werden.
  • Nutzen Sie soweit es geht const Variablen für alle Werte im Programm, welche zur Laufzeit nicht mehr geändert werden. Wichtig: Das gilt z.B. für Grenzen von Schleifen ( for(int i=0; i<iMax;i=i+1)) oder für Sonderzeichen. Bei größeren Programmen biete es sich an die const Variablen mit in einem separaten header zu pflegen.
  • Variablen beginnen mit Kleinbuchstaben.
  • Falls Sie aus mehreren Wörtern zusammengefügt sind, so werden die folgenden Wörter ohne Unterstrich direkt angefügt, aber groß geschrieben. Dies wird auch als „BinnenMajuskel“ oder „camelCase“ bezeichnet.
  • Vermeiden Sie zu allgemeine Namen, wie anzahl, uebergabewert oder string. Sinnvoller sind Namen, wie anzahlBuchstaben, stunden, ausgabeString. Durch die Autovervollständigung (Vorschläge unter dem eingegebenen Text) sind auch längere Namen schnell einzugeben, bzw. mit Cursortasten und TAB auswählbar.
  • Nutzen sie auch bei Zählvariablen aussagekräftige Namen.
  • Auch kann eine zu allgemeine Deklaration kann zu Problemen führen. Schlecht ist z.B. int a;.
  • Es bietet sich an bei der Definition bereits zu initialisieren. Gut ist also bool a=1;.

Beispiel für Variablen

  • Teilen Sie Ihr Projekt in sinnvolle Unterstrukturen. Diese sind meist Funktionen. Die Unterstrukturen sollten nicht zu groß werden, um die Übersichtlichkeit zu bewahren.
  • Bei größeren Programmen ist auch die Aufteilung in mehrere Dateien sinnvoll, also z.b. main.c, LED.c, motorDriver.c. Dabei sollte darauf geachtet werden, dass globalen Variablen und Konstanten jeweils nur im Kontext der einzelnen Dateien genutzt werden und, dass header-Dateien angelegt werden. Das ermöglicht ein separates Testen der unterschiedlichen Dateien (z.B. mit einer Datei testLED.c, welche LED.h include't).
  • Nutzen Sie den Zeileneinschub den Microchip Studio automatisch anbietet.
  • Für die Benennung von Funktionen bietet sich - wie bei Variablen - camelCase an. Zum leichteren Verständnis sollten die Funktionsnamen aus Objekt(e) und Verb zusammengesetzt werden (z.B. bool isI2cMessageNotSent() oder void sentI2cMessage()).
    Damit wird der Code besser lesbar, Z.B. if (isI2cMessageNotSent()) sentI2cMessage()
  • Vermeiden Sie zu viele Leerzeilen.
  • Stellen Sie auch jeder Funktion eine kurze Beschreibung voran. Aus dieser sollte hervorgehen, was Sinn und Zweck der Funktion ist.

Beispiel für Anweisungsblöcke

  • Es ist sinnvoll Deklaration und Definition der Schleifenvariable direkt in den if-Befehl zu packen. Also: for(uint8_t i=0; i<MAX; i=i+1) . Damit wird der Code etwas kompakter.
  • Vermeiden Sie Zugriffe auf Bereiche von Arrays, welche nicht definiert wurden. So erhält man bei einem Array uint8_t array[10]; mit Zugriff auf array[-1], array[10] oder array[21] keine Werte des eigentlichen Array, sondern Werte von anderen Variablen. Ein Beschreiben dieser Bereich kann zu unerwarteten Werten in anderen Variablen führen.

Beispiel für Arrays, Schleifen und Abfragen

  • Ziel ist ein leicht lesbarer und wartbarer Code. Halten Sie deswegen alle Funktionen schlank - auch void main(). Als Faustformel wären 100 Zeilen für eine Funktion zu groß, 20…50 Zeilen gut.
  • Versuchen Sie sinnvolle Unterfunktionen zu programmieren. Trennen Sie Eingabe, Verarbeitung und Ausgabe.
  • Überlegen Sie sich immer wenn Sie im Code Copy-Paste nutzen, warum dies nicht als Unterfunktion lösbar ist.

Beispiel für ähnliche Zeilen

  • Prüfen Sie, ob aufeinanderfolgende, ähnliche if-Anweisungen sich nicht direkt über Arrays lösen lassen (Beispiel Verzweigungen 1). Wählen Sie bei Verzweigungen statt vielen if-Anweisungen mit ähnlichen Bedingungen Switch-Case-Anweisungen (Beispiel Verzweigungen 2). Falls diese nicht möglich sind, eine For-Schleife und Arrays (Beispiel Verzweigungen 3).
  • Auch Switch case kann auch durch verschiedene Vereinfachungen noch verbessert werden.

Beispiel für Verzweigungen 1 - Umwandlung in Array

Beispiel für Verzweigungen 2 - Umwandlung in Switch-Case

Beispiel für Verzweigungen 3 - Optimierung von Switch-Case

Beispiel für Verzweigungen 4 - Umwandlung in For-Next

  • Falls Sie if-Ausdrücke nutzen, für welche vorherige Fälle nicht gelten, so überprüfen Sie folgende Punkte. Wenn die if-Ausdrücke ausschließlich gegenseitig ausschließende Bedingungen beinhalten, so nutzen Sie „else if“ (Beispiel Verzweigungen 4). Falls unabhängig von den Bedingungen Anfangs- oder Endanweisungen immer ausgeführt werden, so sollten diese nicht im if-Ausdruck stehen (Beispiel Verzweigungen 5).

Beispiel für Verzweigungen 4 - Verwenden von Else if

Beispiel für Verzweigungen 5 - Reduzieren der Anweisungen

  • Nutzen Sie im main() immer eine Endlosschleife, um an den Anfang zurückzukehren. Bitte verwenden Sie dazu nicht den Aufruf von main() in main()! Der Mikrocontroller legt dabei jedesmal neu Rücksprungadresse und Variablenzustände im Speicher ab und füllt diesen so auf. Korrekt wäre die Verwendung einer Endlosschleife.
  • Verwenden Sie nie den Goto-Befehl. Wird durch diesen eine Schleifenende u.ä. übersprungen, so werden die Speicherbereiche für die nur dort verwendeten Variable nicht freigegeben.
  • Wenn Sie aus verschachtelten Schleifen zurückkehren wollen, sollten Sie break und ein Flag nutzen.

Beispiel für Schleifen 1 - main()

Beispiel für Schleifen 2 - Abbrechen von verschachtelten Schleifen

  • _delay_ms() und _delay_us() sind zu 99% nicht notwendig. Verwenden Sie stattdessen Interrupts, bzw. Timer.
    Es sollten durch Interrupts Takte angelegt werden, z.B. takt10ms, takt100ms, takt1s welche nur im main() abgefragt werden.
  • Häufig bietet es sich an, in den Takten dann auch Verzweigungen in einer Zustandsmaschine aufzubauen (z.B. mit Zustandsvariablen wie outputState: switch(outputState) {case initOutput: outputState++; ... break; case startOutput: outputState++; ... break; }).
  • Wenn Sie Zahlen in Variablen speichern und diese auch mathematisch weiterverwenden, so wandeln Sie diese Variable erst bei der Ausgabe in das ASCII-Format um.

Beispiel für Variablen mit Ausgabe

Bewertung

Zur Bewertung lege ich diese Checkliste (xls-File) als Maßstab an.