Von der Aufgabe zum Programm

Status
Für weitere Antworten geschlossen.

oldmax

Mitglied
Premium Benutzer
03. Nov. 2008
595
15
18
Landkreis Göttingen
Sprachen
  1. Assembler
Hi
Nun, da ich mich angeboten habe, etwas zum Forum beizutragen und nicht nur alles die "anderen" machen zu lassen, möchte ich nun mein Versprechen einlösen. Ich hoffe, ich hab mich nicht allzuweit aus dem Fenster gelehnt und die richtigen Worte gefunden.

Wie macht man aus einer Aufgabe ein Programm

Bei dieser Fragestellung müssen wir davon ausgehen, die Hardware existiert und es gilt nur die reine Programmiertechnik zu betrachten.
Da die Aufgaben sehr unterschiedlich sind und eine genaue Beschreibung nicht vorliegt, werde ich versuchen, mit einem Blackboxverfahren eine einfache und universell einsetzbare Vorgehensweise zu erklären, die unabhängig von jedweder Programmiersprache ist.
Eine wichtige Regel: Struktur.
Egal, welche Programmiersprache benutzt wird, es ist wichtig, den grundsätzlichen Aufbau zu kennen und die Gerüste nach einem festen Schema (Siehe Skizze) aufzubauen.

Der Deklarationsabschnitt ist in allen Programmiersprachen vorgegeben. Dort werden Variablen und deren Formate festgelegt. Es sind die Parameter, die ein Programm für seine Arbeit braucht. Damit der entsprechende Speicherbedarf ermittelt und reserviert werden kann, muß dem Compiler bekannt sein, was berücksichtigt werden muß. Auch externe Programme sind dort angegeben, die vom Compiler eingebunden werden.
 

Anhänge

  • programmblock.JPG
    programmblock.JPG
    8,3 KB · Aufrufe: 41
Namen....

Namen vergeben

Beginnen wir mit Variablennamen.
Dies mag eines der schwierigsten Kapitel sein, einerseits schlüssige, andererseits nicht zu große Namen zu finden.
Fehler 1: die Faulheit lässt grüßen.
Variablen „a“ , „B“, „C“ usw. sind nicht aussagekräftig und spätestens nach der 96 Programmzeile überlegt man, ob „A“ oder doch „x“ zu benutzen ist.
Fehler 2: Perfektionismus.
Variablen „ErsteZwischenWertBerechnung“ ist viel zu lang und unleserlich. Da hilft auch keine Trennung durch Unterstriche in folgender Art : „Erste_Zwischen_Wert_Berechnung“. Außerdem ist fraglich, ob diese Variable nicht nur temporär zur Zwischenspeicherung benötigt wird. Dann würde auch „Zwischen_Wert“ völlig genügen.
Fehler 3: englische Namen.
Nicht, das ich da grundsätzlich gegen bin, nein, im Gegenteil. Oft ist ein englischer Ausdruck kürzer und prägnanter. Aber, es gibt da klitzekleine Tücken, die nicht immer vom Compiler als Fehler erkannt und gemeldet werden. Da gibt es Schlüsselwörter der Programmiersprache. Und allzu leicht hat man sich eines davon ausgesucht und wundert sich über das merkwürdige Verhalten der Software.
Deshalb sollte man sich treu bleiben und nicht durch „verenglischung“ Professionalität vortäuschen. Solange keine gewerbliche Nutzung angestrebt wird, ist die Muttersprache immer noch die erste Wahl.
----------------------------------------------------------------
Für die Unterprogramme sind ebenfalls Namen zu vergeben. Hier sollte der Name die Information tragen, welche Aufgabe dieser Codeabschnitt zu erledigen hat. Diese Technik der kleinen Programmschnipsel ist auf allen Systemen möglich.
Der Vorteil von Unterprogrammen ist nicht zu unterschätzen. Ein paar Zeilen weiter werde ich das Blackbox-Verfahren erklären und genau diese Technik ist bestens dafür geeignet, mit Unterprogrammen nach und nach ein Programm zu füllen. Es gibt Momente, da ist der Lösungsweg nicht auf Anhieb klar. Vielleicht liegt es an mangelnder Kenntnis der Sprache oder einfach nur an der fehlenden Idee. Ein Unterprogramm kann in eine Programmstruktur eingebunden und der Rückgabewert, wenn erforderlich statisch belegt werden. Der Inhalt ist bis auf die Wertzuweisung der Rückgabe überhaupt noch nicht programmiert und trotzdem ist das gesamte Programm lauffähig. Ist ein solcher Codeschnipsel einmal getestet, kann man diesen Teil beiseite legen, er wird immer seine Aufgabe erfüllen. Fehlerquellen können nun nur noch in den Eingangswerten liegen, aber nicht am Programm selbst.
 
Einstieg ins Programm

Einstieg ins Programm
Der erste Schritt, bevor wir ein Programm durchlaufen lassen, ist die Startbedingung festzulegen. Ein anderer Name dafür ist „Initialisierung“.

In diesem Block werden die Variablen auf den Startwert gesetzt, die Parametrierung der Timer und Schnittstellen vorgenommen und auch die Register mit Startwerten versehen.
Je nach Sprache sind die Schwerpunkte anders verteilt, aber in jedem Programm wird irgendwo vorbereitend auf einen sauberen Start die Eigenschaften der verschiedenen Elemente gesetzt.
Bewegt man sich auf der Seite eines Controllers sind es die internen Register und Ports , die auf Startwerte gesetzt werden. In Hochsprachen in Objekt – orientierter - Programmierung (OOP) ist es die Initialisierung für jedes erzeugte Objekt.
Um unabhängig von den Sprachen zu bleiben, ist der Hinweis für die Initialisierung ausreichend. Den Umfang legt man bei der Projektbetrachtung fest.
Auch innerhalb der Initialisierung ist es sinnvoll, einige Grundwerte in Unterprogrammen zuzuweisen. Innerhalb eines Programms ist es durchaus üblich, diverse Werte zu „nullen“ oder auf Anfangswert zu stellen. Ein Neustart würde dies auch erledigen, wäre aber nicht unbedingt sinnvoll.
Dazu ein Beispiel:
Die Rundenzählung und Zeiterfassung bei einer Modellrennbahn. Während Karl schon fleißig seine Runden dreht, möchte Gisela auch mal üben. Dazu muß ihre Spur zurückgesetzt werden, damit die Werte für ihr Training gültig sind. Die Werte von Karl dürfen nicht verändert werden. Also wird man eine Initialisierung für Jede Bahn einzeln durchführen. Hier bietet sich an, eine Routine mit unterschiedlichen Parametern aufzurufen. Wenn dieses Unterprogramm für Bahn 1 und für Bahn 2 fehlerfrei arbeitet, dann wird sie auch für Bahn 199 fehlerfrei sein.
Durch dieses Vorgehen macht man sich frei von zig Programmteilen, die auch alle gepflegt werden müssen. Um dies zu verdeutlichen, erweitere ich das Beispiel:
Zu den erfassten Werten kommt noch eine weitere Stelle hinzu. Die Rundenzeiten sollen um die Gesamtzeit erweitert werden.
Bei einem Aufruf eines Unterprogramms mit Parametern brauche ich diese Routine nur einmal anpassen. Habe ich für x-Bahnen jeweils eine Reset-Routine geschrieben, muß ich auch x Programmteile ändern und habe auch x-mal die Möglichkeit, Fehler einzubauen.
 
Die Programmschleife

Kommen wir nun zum eigentlichen Programm.
In allen Anwendungen reden wir von einem zyklischen Durchlaufen einer Programmschleife. Das hier nicht die Initialisierung eingeschlossen ist, versteht sich von selbst. Schließlich will man ja auf berechnete Werte aufbauen und nicht immer wieder von „0“ anfangen. Trotzdem folgt auch die Programmschleife einem festen Schema. Der Begriff „EVA“ hat sich hier geprägt und dies gilt es nun zu erklären.
Dazu eine kleine Skizze des Programms.

Die Initialisierung haben wir bereits besprochen, daher wenden wir uns nun dem Programm zu und beschreiben die drei Blöcke.
Bevor etwas verarbeitet werden kann, muß man wissen, welche Informationen dafür notwendig sind. Zu diesen Informationen gehören alle Signale, die von Sensoren geliefert werden, ob digital oder analog. Auch Kommunikationswege werden hier ihre Schnittstelle finden, wo sie ihre Daten loswerden können. Also, alle Informationen, die ein Programm benötigt, landet in der Blackbox „E“.
Für die Eingänge legt man sich am besten eine Tabelle an, in der man die einzelnen Signale beschreibt. Auch hierzu eine kleine Skizze.

So etwa könnte eine Liste der Eingangsinformation aussehen.
Je genauer diese Tabelle geführt wird, umso leichter behalten wir den Überblick. Es ist sowieso besser, man lässt ein bisschen die Doku parallel mitlaufen. Statt Eingänge könnten hier auch Variablen stehen, wenn z.B. in Programmen für PC gar keine I/O programmiert werden.
Da der Mittelteil, das „V“ doch sehr umfangreich wird, gehe ich erst einmal einen Schritt weiter zu den Ausgaben. In der Regel weiß ich ja, welches Ergebnis das Programm liefern soll. Also kann ich ebenfalls eine Tabelle anlegen und die Signale dort beschreiben. In PC-Programmen sind dies die Ausgabefenster, in Controllern die Hardware-Ports. Damit bin ich auch schon soweit durch, das ich mir das Gebilde ansehen kann. Modere Menschen reden da von Layout… ist aber nix anderes wie Ansicht. Ausgabefenster müssen „gefallen“ und „praktisch“ sein. Auch spielt hier Zugehörigkeit eine Rolle. Schließlich möchte man sich leicht zurechtfinden. So macht es keinen Sinn, in einer Adressdatei von Händlern eine Warenliste mitzuführen. Also wird man eine Ansicht für die Adressen aufbauen und eine Ansicht für Waren. Händlerinformation wird dann nur mit Namen, nicht mit der gesamten Adresse geführt. Datenblätter werden ebenfalls in eigene Ausgabefenster gesetzt, Referenzen zur Ware erfolgt auch hier nur mit dem Bezeichner. Nun ja, ein klein wenig gelogen hab ich, da Waren schlecht über den Namen referenziert werden können, die Vielfalt ist zu riesig, um immer eine Eindeutigkeit zu garantieren. Hier ist das Zauberwort „Artikelnummer“. Die kann zwar kein Mensch immer zuordnen, aber das macht ja Gott sei dank auch das Programm. Nach außen werden Klartexte geliefert und intern mit Nummern referenziert. Genug der Rede über PC-Programme. Ich denke, viel interessanter ist in diesem Forum der Bezug zum µC.
Hier ist es nicht anders, nur das die Hardwaresignale angeordnet werden müssen. Nicht nach Schönheit oder Zugehörigkeit, sondern hier steht im Vordergrund doch das Layout der Platine. Wie sind die Leiterbahnen am günstigsten zu führen, wo sind die Schnittstellen zur Außenwelt meiner Schaltung. Dem Programmierer ist es egal, wie die Ports beschaltet sind, die Zuordnung wird in einer kleinen Routine durchgeführt und intern in den Bytes so abgelegt, das wieder eine Gruppierung stattfinden kann. Dazu dienen die logischen Funktionen mit Und- und Oder – Anweisungen. Dies gehört aber in den „V“ –Teil.
Hier noch die Skizze für die Ausgabetabelle

Der „V“ Block
Vielleicht gelingt es mir tatsächlich, einen Weg zu einem Programm zu zeigen, ohne eine einzige Programmzeile in irgendeiner Sprache zu benutzen. Ich habe die Blöcke „E“ und „A“ beschrieben und denke, damit dürfte es einigermaßen ausreichen. Alles Weitere geht dann schon in die speziellen Anwendungen und hier ist ja nur der Weg gefragt. Schauen wir uns nun den „V“-Block an. Hier ist nun alles drin, was aus externen Signalen aufbereitet werden muss, um die gewünschte Ausgabe zu erhalten. In den „E“ und „A“ Blöcken haben wir Tabellen für die Signale oder Darstellungselemente angelegt. Warum nutzen wir diese Technik nicht auch für die Verarbeitung ? Klar, eine Tabelle muss her, eine Tabelle, die beschreibt, wie aus der Eingangs- eine Ausgabeinformation wird. Auch dies wird in einem Anhang in einer Skizze dargestellt.
Allerdings beschreiben wir nun keine Signale, sondern Routinen, die bestimmte Zwecke erfüllen müssen. Beginnen wir mal mit der Beschreibung einer Routine, die ein eingesammeltes Bit aus irgendeinem Byte herausfiltert und in ein anderes Byte einfügt. Eine solche Routine hat 4 Parameter, ein Eingangs- und ein Ausgangsbyte sowie jeweils die Bitstelle von der Quelle und vom Ziel.
Nennen wir die Routine einmal Bit_Select. Nun brauchen wir entweder Variablen oder schreiben die Information in Register. Dies tragen wir in die Tabelle ein, auch das Register, in welchem dann das Ergebnis steht. Da ich aber keine Programmzeile benutzen möchte, zeige ich lediglich den Weg anhand einer… na klar, Blackbox.
 

Anhänge

  • EVA.JPG
    EVA.JPG
    13,4 KB · Aufrufe: 24
  • Tabelle_ein.JPG
    Tabelle_ein.JPG
    35,3 KB · Aufrufe: 31
  • Tabelle_aus.JPG
    Tabelle_aus.JPG
    38,6 KB · Aufrufe: 20
  • Tabelle_Prog.JPG
    Tabelle_Prog.JPG
    36,3 KB · Aufrufe: 20
ein typischer Programmblock

Die Betrachtung eines Programmblocks

Um eine Routine für den Programmblock in der Skizze zu schreiben, reichen die Informationen. Wir wissen, mit einem Und kann man ein einzelnes Bit ausmaskieren. Das Quellbit steht beispielsweise auf Platz 3, also muß es auf Platz 0 verschoben werden. Dann erfolgt ein Und mit der Maske 0b00000001. Dadurch wird nun nur der Status aus unseren gesuchten Bit übernommen. Anschließend schieben wir das Bit 0 auf die Zielposition, z. B. Platz 6. Nun erfolgt ein Oder mit der Zielvariable und damit ist diese Blackbox ein funktionierendes kleines Unterprogramm. Ach ja, ein Fehler ist da noch drin…. Wir können ja nicht immer davon ausgehen, das im Byte mit dem Ergebnit das Bit 6 vorher „0“ war und bei dem Status vom Quellbit =“0“ auch eine „0“ liefert. Deshalb ist es ratsam, das Zielbyte zuerst zu leeren und dann in der Reihenfolge zu besetzen .

Wie bei dieser Vorgehensweise leicht zu erkennen ist, je kleiner die Programmblöcke, umso leichter die Programmierung. Betrachtet man den Block“V“ wird man beim Versuch, daraus ein Programm zu schreiben, schlichtweg verzweifeln. Die vielfältigen Bedingungen werden uns immer wieder zeigen, hier ist etwas nicht berücksichtigt, da muss etwas angepasst werden. Und eh man sich versieht, ist der Durchblick zum Teufel. Die Kunst, Aufgaben in kleine überschaubare Routinen zu packen, macht den Programmierer, nicht die Anzahl der Programmzeilen. Um dieses zu lernen, kann ich nur eindringlich die Blackbox empfehlen. Aber was hat es auf sich, mit diesem schwarzen Kasten?
 

Anhänge

  • kleines Programm.JPG
    kleines Programm.JPG
    16,7 KB · Aufrufe: 29
die Blackbox

Arbeiten mit der Blackbox

Da ist dieser Block „V“. Den malen wir nun auf ein DinA4, schön groß und fügen ein paar Blöcke ein, Diesen geben wir Namen wie z.B. „Bit_Select“ oder erst einfach nur Box 1 bis Box x . Diese Name stehen für die Aufgaben, welche diese Blöcke, diese Blackboxen, lösen sollen. Dazu überlegen wir, welche Information ist notwendig. Die Information kann man aus einer Tabelle entnehmen oder in die Tabelle nachtragen. Man wird immer wieder auf neue kleine Programmfetzen stoßen, die anfangs in dieser Art nicht erkennbar waren.
So wie es im Beispielblock Bit_Select aufgeführt wurde, kann jeder andere Block gezeichnet werden. Sind wir der Meinung, das Programm weit genug in kleine Schritte zerlegt zu haben, sortieren wir die Blöcke und ordnen sie so an, das ein Fluss zu erkennen ist. Dabei werden Blöcke auch schon mal mehrfach angesprochen.
Sind wir überzeugt, die grobe Struktur vorliegen zu haben, beginnen wir mit der Programmierung der einzelnen Aufgaben.
Stellen wir dabei fest, hier könnte man wieder einen kleinen Block programmieren, sollten wir es tun, auch wenn ein Sprung mehr dabei herauskommt.
Nur wenn wirklich zeitkritische Anwendungen die letzte Nanosekunde fordern, dann ist die Vorgehensweise etwas anders. Da wird nicht mit Parametern der Bitposition gearbeitet, sondern direkt auf die Bitstelle zugegriffen. Kostet mehr Code und Speicher, erspart aber ein paar Befehle und Schleifendurchläufe. In der Summe hat man schnell ein paar mS eingespart.

Dennoch, die Applikation setzt die Bedingungen und wenn Zeit keine Rolle spielt, sollte man sein Programm ruhig in viele kleine Abschnitte zerlegen, die als Unterprogramme dann ihren Dienst versehen. In der Skizze sieht man die Schachtelung der kleinen Programmteile und sehr schnell wird der Umfang eines Programms deutlich. Aber so dokumentiert ist der Überblick jederzeit gegeben.
Im Anhang sind wieder ein paar bildliche Beispiele zu diesem Thema.

Die Skizze verwirrt etwas, aber wenn die Boxen mit Namen ihrer Aufgabe versehen sind und die Variablen ihren wahren Namen tragen, wird das Bild wesentlich klarer. Schließlich, daran sollten wir auch denken, haben wir ja noch die Tabelle, die ebenfalls dazu beiträgt, das der Faden nicht verloren geht. Die Skizze zeigt den Ablauf, die Tabelle verwaltet den Fortschritt der Programmierarbeit.
Es mag etwas ungewöhnlich aussehen, aber umfangreiche Projekte, die oftmals von einem Team in gemeinsamer Arbeit erstellt werden, erfordern nicht nur Schnittstellen für jeden sondern zwingen zur Offenlegung des Fortschrittes. Ich mache mir das für meine Arbeit, auch als Einzelkämpfer, gern zu Nutze, denn im Laufe meiner Erfahrung habe ich festgestellt „Es gibt keine kleinen Aufgaben“.
 

Anhänge

  • Blackbox.JPG
    Blackbox.JPG
    14,7 KB · Aufrufe: 14
  • Blackbox_3.JPG
    Blackbox_3.JPG
    17,1 KB · Aufrufe: 10
  • Blackbox_Strukt.JPG
    Blackbox_Strukt.JPG
    46,9 KB · Aufrufe: 27
Dokumentation und abschließende Worte

Bleibt noch etwas zur Dokumentation zu sagen.
Oft bleibt dieser Punkt auf der Strecke, denn das mühselig erstellte Projekt läuft endlich rund. Die Bedienung ist einfach und unkompliziert….. wirklich? Gebt euer Produkt mal einem Freund…
Es ist erstaunlich, aber bei der ganzen Arbeit ist man so tief eingedrungen, das die Bedienung und Nutzung sternenklar ist. Zumindest die ersten 5 Tage. Legt mal das Gerät ein halbes Jahr zur Seite und es wird deutlich, warum ein bisschen Doku notwendig ist. Beruft euch nicht auf selbsterklärenden Code. Den gibt es nicht, genauso wenig, wie es selbsterklärende Programme gibt.

Zum Abschluss kommt noch ein Wort, das vielleicht besser in der ersten Zeile gestanden hätte- aber dann hättet ihr womöglich nicht weiter gelesen…………..

Ein Programm schreibt man nicht mal eben zwischen zwei Pausen. Es ist ein sehr langwieriges Unterfangen und kann sich über Monate hinziehen. Grad Anfänger unterschätzen den Aufwand. Auch wenn es nur nach einer kleinen Applikation aussieht, schnell sind ein paar hundert Programmzeilen zusammengestellt. Irgendwann erkennt man eine Einbahnstrasse, reißt alles wieder nieder und beginnt von vorn. Jedes mal ein bisschen besser.
Und dann ist es dann soweit, bereit für die Premiere bei den Kumpels und Freunden.
Spätestens bei den ersten Eingabetests stellt man fest, welche Bedienung verhindert und abgefangen werden muss. Dafür eignen sich bestens technisch völlig Unbegabte aus dem Freundeskreis. Drückt ihnen euren ganzen Stolz in die Hand und es dauert keine 5 Minuten zu der Erkenntnis, es gibt ihn, den DAU.:confused:
(Dümmster anzunehmender User) :stupido3:
Und den muss man überlisten. Wenn es tatsächlich ein „Wow“ und „ey, echt geil“ zu hören gibt, ja, dann scheint man das meiste richtig gemacht zu haben. :flowers:
Am Schluss möchte ich noch einmal kurz zusammenfassen:
Weg von der Aufgabe zum Programm
1. Struktur einprägen
-> Initialisieren -> Programmschleife mit Einlesen, Verarbeiten und Ausgeben
2. Informationen ordnen und beschreiben
3. Ausgaben definieren und festlegen
4. Blackboxverfahren für Verarbeitung
-> Aufgaben splitten , in viele kleine Schritte zerlegen
-> für die Verarbeitung die Reihenfolge ordnen
-> Routinen mit Parametern versehen und beschreiben
5. Dokumentieren

Eigentlich ist doch die Zusammenfassung allein schon aussagekräftig genug, warum hab ich da bloß tagelang am Text rumgebastelt?:cheers:
Gruß oldmax
 
Status
Für weitere Antworten geschlossen.

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)