Forum: Mikrocontroller und Digitale Elektronik Design patterns für Embedded Software


von Pit (Gast)


Lesenswert?

Hallo zusammen,

Ich bin jet ein gutes Jahr in der Softwareentwicklung für Landmaschinen 
tätig. Hab bisher meine C-KnowHow deutlich verbessern können gegenüber 
dem Studium. Der nächste Schritt wäre nun zu wissen, wie man "gute" 
Software schreibt bzw baut.
Wir schreiben nur die Applikation für die Maschinen, da die Bios 
Funktionen vom Hersteller im Form einer Lib kommen. Meiner Meinung ist 
die Applikation etwas veraltet und pseudo C++. So gibt es zum Beispiel 
zyklische Funktionen, bei denen Parameter neu geladen oder kontrolliert 
werden. Beispielsweise wenn ich einen Ringpuffer mit 10 Stellen 
initialisiere wird jede Sekunde überprüft, ob sich was an der Grösse 
geändert hat. Auf die Frage warum sowas gemacht wird heisst es nur, dass 
es schon immer so gemacht wird.
Ausserdem gibt es für jedes Modul ein eigenes Objekt. Diese Objekt wird 
dann jeder Funktion als Übergabeparameter im Modul übergeben. Beispiel: 
Es gibt eine Hydraulik.c und .h und dort ist das Objekt Hydraulik_t 
definiert mit allen Variablen, die die Hydraulik betreffen. Somit kann 
man in jeder Funktion auf jede Variable zugreifen.
Da ich nächstes Jahr ein neues Projekt bekomme wollte ich mal den 
kompletten Code anpassen. Daher habe ich die Frage ob ihr mir gute 
Bücher für den Bereich Embedded Software empfehlen könnt. Hab schon nach 
Design patterns geschaut aber die Bücher waren meistens nicht gut 
bewertet. Ich möchte mehr darüber erfahren wie man seine Software besser 
strukturiert, generell wie man sie aufbaut.

Gruss Pit

von c-hater (Gast)


Lesenswert?

Pit schrieb:

> Ich bin jet ein gutes Jahr in der Softwareentwicklung für Landmaschinen
> tätig. Hab bisher meine C-KnowHow deutlich verbessern können gegenüber
> dem Studium. Der nächste Schritt wäre nun zu wissen, wie man "gute"
> Software schreibt bzw baut.

Definitiv jedenfalls nicht mit C.

Entweder man hat ein vergleichsweise großzügig mit Resourcen 
ausgestattes Zielsystem, dann benutzt man eine OO-Programmiersprache, 
notfalls halt C++ (wenn man glaubt C zu können, was ich in 99,5% aller 
Fälle sowieso erstmal bezweifeln würde).

Oder man hat das nicht, dann macht man das mit einer Sache, die im 
Prinzip dasselbe ist wie C, mit einem Macro-Assembler.

C war immer Dreck, ist Dreck und wird immer Dreck bleiben. Nicht 
Fleisch, nicht Fisch, für nichts wirklich zu gebrauchen. Zu 
undeterminiert im Timing-Verhalten, zu ineffizient bei der 
Codegenerierung, um Asm ersetzen zu können, zu schwach typisiert und 
ohne OO-Konzepte, um richtige Hochsprachen ersetzen zu können.

Braucht man nicht und will man nicht. (Sollte man aber nichtsdestotrotz 
beherrschen, zumindest um hinreichend kompetent lästern zu können, aber 
auch sonst ist es durchaus oft hilfreich...)

Insofern war es also zumindest nicht völlig umsonst, daß du dir auch 
C-Kenntnisse angeeignet hast. Viel wichtiger dürfte aber die hoffentlich 
ganz allgemein gewonnene Routine bei der Analyse von Problemen und der 
Umsetzung mit den Mitteln irgendeiner gegebenen Programmiersprache 
sein. Wirklich Programmierer ist man nämlich erst, wenn einem die 
Programmiersprache im Prinzip egal ist, man den Kram also in jeder 
umsetzen kann, solange man nur eine Referenz der jeweiligen Sprachmittel 
hat. Dann entscheidet die konkret verwendete Sprache nämlich nur noch 
über zwei Sachen:

1) Wie lange die Implementierung dauert.
2) Wie effizient das Ergebnis zur Laufzeit ist.

Und wenn man soweit ist, wird die Sache sofort sonneklar: Normaler Kram 
in einer OO-Sprache, zeitkritisches Zeug in Asm. Und wenn die Resourcen 
sehr knapp sind: alles in Asm.

Fazit: No need for C.

von sb (Gast)


Lesenswert?

War'n bei Dir Pilze im Müsli ?

von Fabian O. (xfr)


Lesenswert?

Pit schrieb:
> So gibt es zum Beispiel
> zyklische Funktionen, bei denen Parameter neu geladen oder kontrolliert
> werden. Beispielsweise wenn ich einen Ringpuffer mit 10 Stellen
> initialisiere wird jede Sekunde überprüft, ob sich was an der Grösse
> geändert hat. Auf die Frage warum sowas gemacht wird heisst es nur, dass
> es schon immer so gemacht wird.

Das klingt relativ unsinnig. Wenn keiner einen Grund dafür nennen kann, 
dann mach es in Deinem eigenen Projekt einfach nicht.

Pit schrieb:
> Ausserdem gibt es für jedes Modul ein eigenes Objekt. Diese Objekt wird
> dann jeder Funktion als Übergabeparameter im Modul übergeben. Beispiel:
> Es gibt eine Hydraulik.c und .h und dort ist das Objekt Hydraulik_t
> definiert mit allen Variablen, die die Hydraulik betreffen. Somit kann
> man in jeder Funktion auf jede Variable zugreifen.

Das ist hingegen nicht verkehrt, sofern den Funktionen ein Zeiger auf 
das Objekt übergeben wird und nicht das Objekt selbst. Genau so macht es 
C++ mit dem this-Zeiger auch, nur dass die Syntax etwas kompakter ist:
1
// C:
2
hydraulik_mach_etwas(&meine_hydraulik, 42);
3
// C++:
4
meine_hydraulik.mach_etwas(42);

Auf Mikrocontrollern muss man ggf. halt abwägen, ob man wirklich mehrere 
Instanzen braucht oder nicht. Wenn ein Objekt garantiert nur einmal im 
System vorkommt, kann man es sich sparen.

Allgemein halte ich es für das wichtigste, die Prinzipien "Starke 
Bindung, lose Kopplung" sowie "Information Hiding" so gut es geht 
umzusetzen. Sprich zusammengehörigen Code in einer Klasse bzw. einem 
Modul sauber kapseln und mit einer möglichst einfachen und kompakten 
Schnittstelle versehen. Außerdem die Module nach Schichten trennen (z.B. 
Mikrocontroller-Hardwareabhängig -> Hardwareabstraktion -> Ansteuerung 
einer (externen) Komponente -> Programmlogik). Aber auch aufpassen, dass 
man sich nicht "verkünstelt" und trivialen Code in 20 Schichten 
aufbläht.

Da den richtigen Mittelweg zu finden, abgegrenzte Komponenten zu 
identifizieren, sinnvolle Schnittstellen zu definieren und daraus 
wiederverwendbare Module zu machen, ist dann hauptsächlich Erfahrungs- 
und imo auch Talentsache. Am Ende sind es immer 
Einzelfallentscheidungen, das kann einem kein Buch abnehmen.

Aus meiner Sicht ist noch sehr hilfreich, sich viel mit fremden Code und 
vor allen dessen Schnittstellen zu beschäftigen. Bei guten Interfaces 
ist es leicht, sie richtig zu benutzen, und schwer, sie falsch zu 
benutzen. Da bekommt man mit der Zeit ein Gefühl für und kann sich gute 
Konstruktionen abschauen, aber auch aus "Anti-Patterns" lernen.

Einen Buchtipp speziell für Embedded-Systeme habe leider nicht. 
Klassiker wie "Design Patterns" von der Gang of Four zielen eher auf 
dynamische Software, bei der zur Laufzeit mit Objekten hantiert wird, 
die die Struktur der Anwendung bestimmen. Das ist bei interaktiven 
PC-Anwendungen oft der Fall. Embedded-Systeme sind dagegen 
typischerweise statisch aufgebaut und maximal die Eingabedaten 
dynamischer Natur, aber nicht das Zusammenspiel der Systemkomponenten an 
sich. Man legt also bei einem Embedded-System z.B. weniger Wert darauf, 
dass man zur Laufzeit neue Hydraulikmodule einfügen kann, sondern eher, 
dass sich Änderungen auf möglichst wenig, zentralen Code beschränken und 
man Code in verschiedenen Projekten wiederverwenden kann.

Außerdem basieren viele der klassischen Design-Patterns auf Polymorphie 
mit virtuellen Methoden, was in C so eh nicht geht (bzw. nur sehr 
umständlich mit Funktionszeigern) und auf Mikrocontrollern auch recht 
teuer (Laufzeit, vor allem aber RAM) ist. Falls Du das Buch nicht 
kennst, ist es aber trotzdem eine Lektüre wert, auch wenn man die 
Lösungen dann nicht 1:1 so umsetzt.

von W.S. (Gast)


Lesenswert?

Pit schrieb:
> Hab bisher meine C-KnowHow deutlich verbessern können gegenüber
> dem Studium. Der nächste Schritt wäre nun zu wissen, wie man "gute"
> Software schreibt bzw baut.

Gute Software hängt zumeist NICHT von der verwendeten Programmiersprache 
ab. Mit Einschränkungen, die man kurz zusammenfassen kann: Die 
Programmiersprache sollte dem Vorhaben angemessen sein.

Also wäre z.B. C++ auf nem gewöhnlichen µC eher unangemessen, weil das 
dort völlig übliche Kreieren und wieder Zerstören von Objekten auf dem 
Heap etwas ist, das man in den meisten Fällen in einer µC-Firmware 
garnicht braucht. Auch all die Funktionalität zum E/A über 
Standardkanäle paßt eher zu einem Kommandozeilen-Programm auf dem PC als 
zu einer Firmware im µC.

Das Gegenteil, nen 32 Bitter komplett in Assembler programmieren zu 
wollen, wäre ebenso unangemessen.

Also: schlichtweg auf dem Teppich bleiben, ist der erste Schritt zu 
gutem Code. Es läuft auf C hinaus, denn C gibt's und ist benutzbar, 
sowas wie Pascal wäre auch benutzbar, gibt's aber nicht. Die Auswahl ist 
halt nicht groß, gelle?

Als dringlichstes rate ich zu sinnvoller Modularisierung. Also in sich 
geschlossene Programmteile für jeweilige Gruppen von Berechnungen und 
Treiber für die diversen Peripheriefunktionen, derart, daß sie ohne Wenn 
und Aber und ohne Blockierungen funktionieren, ohne daß man von außen 
sich um deren ganzen internen Krempel kümmern muß. Da wird von vielen 
Programmiereichen ziemlicher Mist betrieben, weil sie die verschiedenen 
Funktionsbereiche einer Firmware nicht auseinander kriegen.

Als nächstes rate ich zu spezialisierten Konvertierungen, also Verzicht 
auf sowas wie sprintf und Konsorten.

OK, ob dir meine Einlassungen bei solchen Vorgaben wie "hydraulik_t" 
wirklich helfen, ist fraglich.

W.S.

von Falk S. (falkschilling)


Lesenswert?

Pit schrieb:
> Ich bin jet ein gutes Jahr in der Softwareentwicklung für Landmaschinen
> tätig.

So, du sprichst ISOBUS, sehr lobenswert. :)

> Hab bisher meine C-KnowHow deutlich verbessern können gegenüber
> dem Studium. Der nächste Schritt wäre nun zu wissen, wie man "gute"
> Software schreibt bzw baut.
> Wir schreiben nur die Applikation für die Maschinen, da die Bios
> Funktionen vom Hersteller im Form einer Lib kommen. Meiner Meinung ist
> die Applikation etwas veraltet und pseudo C++.

Google doch einfach mal nach populären C-Bibliotheken/Anwendungen und 
schau dir mal an, was die so gemacht haben. Gerade für GNU/Linux oder im 
BSD-Bereich gibt es so hervorragende Bibliotheken, die man sich mal 
anschauen kann, das hilft schon sehr.

> So gibt es zum Beispiel
> zyklische Funktionen, bei denen Parameter neu geladen oder kontrolliert
> werden. Beispielsweise wenn ich einen Ringpuffer mit 10 Stellen
> initialisiere wird jede Sekunde überprüft, ob sich was an der Grösse
> geändert hat. Auf die Frage warum sowas gemacht wird heisst es nur, dass
> es schon immer so gemacht wird.

Was am Anfang kein schlechter Ratschlag ist, sich an den Code 
anzupassen, der vorhanden ist, anstelle neue Baustellen aufzureißen.

> Ausserdem gibt es für jedes Modul ein eigenes Objekt. Diese Objekt wird
> dann jeder Funktion als Übergabeparameter im Modul übergeben. Beispiel:
> Es gibt eine Hydraulik.c und .h und dort ist das Objekt Hydraulik_t
> definiert mit allen Variablen, die die Hydraulik betreffen. Somit kann
> man in jeder Funktion auf jede Variable zugreifen.

Hmm - und du möchtest eine kleine objektorientierte Architektur nur mit 
Strukturen. Jo, kann man machen.

> Da ich nächstes Jahr ein neues Projekt bekomme wollte ich mal den
> kompletten Code anpassen.

Da haste dir was vorgenommen.

> Daher habe ich die Frage ob ihr mir gute
> Bücher für den Bereich Embedded Software empfehlen könnt. Hab schon nach
> Design patterns geschaut aber die Bücher waren meistens nicht gut
> bewertet.

Kannst du nicht mal in 'ne Fach-/Universitätsbuchhandlung respektive 
Bibliothek gehen und dir dort mal Bücher zum Thema anschauen, bevor du 
welche kaufst?

Ansonsten: der Klassiker ist E. Gamma "Entwurfsmuster", sehr zu 
empfehlen ist auch "Entwurfsmuster von Kopf bis Fuss" vom 
O'Reilly-Verlag. Letzteres ist für Einsteiger ziemlich gut.

Das betrifft aber grundsätzlich objektorientierte Programmierung. So wie 
ich dich verstehe, nutzt du ANSI-C zur Programmierung. Gerade im 
ISOBUS-Bereich würde ich dir da "Test-Driven-Development for Embedded-C" 
von James Grenning empfehlen. Bevor du anfängst, Baustellen aufzureißen, 
solltest du da mal einen Blick drauf werfen.

> Ich möchte mehr darüber erfahren wie man seine Software besser
> strukturiert, generell wie man sie aufbaut.

Schreibe SOLIDen Code:

S - Single Responsibility
O - Open/Close
L - Liskov Substitution
I - Interface Segregation
D - Dependency Inversion

Und wie man das mit C macht - siehe Grenning:

http://www.renaissancesoftware.net/blog/

von Max (Gast)


Lesenswert?

Fabian O. schrieb:
> Das ist hingegen nicht verkehrt, sofern den Funktionen ein Zeiger auf
> das Objekt übergeben wird und nicht das Objekt selbst.

Ja es wird ein Zeiger auf das Objekt übergeben. Das an sich "stört" mich 
ja auch gar nicht. Ich habe halt nur ein Problem damit, dass man damit 
zugriff auf alle Variablen hat. Dann sind das ja quasi alles globale 
Variablen und darauf würde ich gern verzichten.

Mir geht es ja auch nicht darum alles auf den Kopf zu stellen. Der 
bisherige Programmstil hat sich ja über Jahre bewährt und ich möchte mir 
einfach nur mal einen Überblick überschaffen, welche Methoden man 
einsetzen kann bzw was sich so an der Programmierung in den letzten 
Jahren geändert hat.

Bücher ausleihen ist hier schwierig, da in der nähe keine Uni ist und 
die Bibliotheken hier haben auch sehr wenig technische Bücher.

Nach Skripten habe ich natürlich auch schon gegoogelt aber da werden 
oftmals nur die Grundlagen vermittelt. Zum Beispiel die Sache mit dem 
Objekt und der Übergabe als Zeiger habe ich so in noch keinem Skript 
gesehen oder das man Enums als State eines Objekts benutzt.

von Mr.Z (Gast)


Lesenswert?

c-hater schrieb:

> Definitiv jedenfalls nicht mit C.
Dummschwätzer. Zeigt nur deine Inkompetenz. Such dir einen anderen Job, 
der dich nicht so überfordert.

von Georg G. (df2au)


Lesenswert?

c-hater schrieb:
> Fazit: No need for C.

Seit einiger Zeit lese ich deine Kommentare. Und immer mehr verfestigt 
sich meine Meinung: Egal, was dein Dealer sagt und egal wie preiswert 
das Zeug ist, such dir was anderes. Es bekommt dir nicht.

Ich habe auch viele Jahre nur in ASM programmiert, meist, weil es nichts 
besseres gab. Für C habe ich mehrere Anläufe gebraucht. Heute möchte ich 
diese Sprache nicht mehr missen. Es ist für mich der optimale Kompromiss 
zwischen maschinennah und übersichtlich.

von Cyblord -. (cyblord)


Lesenswert?

Georg G. schrieb:
> c-hater schrieb:
>> Fazit: No need for C.
>
> Seit einiger Zeit lese ich deine Kommentare. Und immer mehr verfestigt
> sich meine Meinung: Egal, was dein Dealer sagt und egal wie preiswert
> das Zeug ist, such dir was anderes. Es bekommt dir nicht.
>
> Ich habe auch viele Jahre nur in ASM programmiert, meist, weil es nichts
> besseres gab. Für C habe ich mehrere Anläufe gebraucht. Heute möchte ich
> diese Sprache nicht mehr missen. Es ist für mich der optimale Kompromiss
> zwischen maschinennah und übersichtlich.

Die ganze Embedded-Welt programmiert in C. Was juckt dich ein kleiner 
c-hater Troll? Dem ist nicht zu helfen, den ignoriert man.

: Bearbeitet durch User
von Pit (Gast)


Lesenswert?

Falk Schilling schrieb:
> Gerade im
> ISOBUS-Bereich würde ich dir da "Test-Driven-Development for Embedded-C"
> von James Grenning empfehlen. Bevor du anfängst, Baustellen aufzureißen,
> solltest du da mal einen Blick drauf werfen.

Das Buch habe ich mir mal von einem Studenten in unserer Abteilung 
ausleihen lassen. Sieht auf den ersten Blick sehr interessant aus.
Wie gesagt mir geht es nicht darum, alles auf den Kopf zu stellen. Ich 
wollte mir nur Methoden anschauen, um die Software besser in Hinsicht 
auf Lesbarkeit, Wartbarkeit und auch Einfachheit zu machen. Ich werde 
natürlich nicht alles über den Haufen werfen aber ich denke es gibt 
einige Punkte, die man optimieren kann. Einige habe ich schon optimiert 
und andere habe ich wieder auf die alte Art zurückgeschrieben.
Kennt vielleicht einer "Making Embedded Systems" vom O'Reilly Verlag? 
Die Leseprobe find ich auch interessant und generell sind viele Bücher 
von O'Reilly wirklich gut.

von A. B. (funky)


Lesenswert?

Das Buch Testdriven Deleopment Buch habe ich auch, und ja es ist ganz 
gut.
Vor allem wenn man eh neu mit einem Projekt anfängt, kann man da viele 
Tips von beherzigen. Bei bestehenden Sachen ist das erstmal ziemlich 
aufwändig und man sollte sich imho gleich mal davon verabschieden alles 
umstellen zu können.


Ansonsten kann ich noch 
http://www.amazon.de/Automotive-Embedded-Systeme-Effizfientes-Implementierung/dp/3540243399 
empfehlen.

Nicht von dem Automotive im Titel abschrecken lassen. Aber das Buch ist 
meiner Meinung nach ziemlich gut und recht praxisorientiert

von Karl H. (kbuchegg)


Lesenswert?

>  Das an sich "stört" mich ja auch gar nicht. Ich habe halt nur ein
> Problem damit, dass man damit zugriff auf alle Variablen hat.

Wenn das tatsächlich ausgenutzt wird, könnte das allerdings wirklich ein 
Hinweis sein, dass die wesentlichen Konzepte der objektorientierung von 
den Ur-Entwicklern nicht verstanden wurden.

Auch wenn es in C nicht anders geht, darf man auch dann den Zeiger nicht 
als eine Art Freibrief ansehen, dass man jetzt mit den Objekt-Daten 
anstellen kann, was immer man will. So gesehen gewinnst du durch den 
Einsatz eines C++ Compilers schon etwas. Einfach nur dadurch, dass du 
Dinge als protected oder private markieren kannst. Alleine das ist schon 
was wert. Es gibt Dinge, die man in C++ dem Compiler überantworten kann, 
die in C rein nur auf der Disziplin des Programmierers beruhen.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.