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
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.
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.
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.
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/
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.
c-hater schrieb:> Definitiv jedenfalls nicht mit C.
Dummschwätzer. Zeigt nur deine Inkompetenz. Such dir einen anderen Job,
der dich nicht so überfordert.
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.
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.
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.
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
> 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.