Forum: Mikrocontroller und Digitale Elektronik Grundsatz >>> möglichst viel Auslagern in Funktionen?


von __Son´s B. (bersison)


Angehängte Dateien:

Lesenswert?

Hallo.

Im Anhang ein Programm-Auszug zur Spannungsüberwachung/-Steuerung.
Dieses gesamte Programm soll später als "Zusatzmodul" in neue Programme 
eingebettet werden.

Was ist hier eine saubere Programmarchitektur?
Macht es Sinn, diesen gesamten main()-Code, in eine Funktion SpgUeberw() 
auszulagern. Somit würde die "neue" main(), sehr schlank und 
übersichtlich werden.
Nur I/O-Deklaration und SpgUeberw() und neue Code.

: Bearbeitet durch User
von __Son´s B. (bersison)


Lesenswert?

__Son´s B. schrieb:
> Was ist hier eine saubere Programmarchitektur?
> Macht es Sinn, diesen gesamten main()-Code, in eine Funktion SpgUeberw()
> auszulagern.
Gerade als Anfänger ist es mir wichtig, direkt eine 
saubere/übersichtliche Programm-Architektur zu gestalten.

Dazu stelle ich mir vor, so viel wie möglich in einzelne spezielle 
Funktionenn zu stopfen um das main() zu entlasten und schlank zu halten.
Ist der Gedanke falsch, das main() ausschliesslich/vorwiegend für die 
Konfig (Ports, Var, etc.) und der Ansteuerung der einzelnen Funktionen 
vorzusehen?

von Falk B. (falk)


Lesenswert?

@  __Son´s Bersi__ (bersison)

>> Macht es Sinn, diesen gesamten main()-Code, in eine Funktion SpgUeberw()
>> auszulagern.

Ja.

>Gerade als Anfänger ist es mir wichtig, direkt eine
>saubere/übersichtliche Programm-Architektur zu gestalten.

Dann tu das. Aber wenns geht mit sinnvollen Namen für Funktionen. Da 
muss man heute nicht mehr krampfhaft abkürzen. Der Trend geht schon 
lange zu eher längeren, aber selbsterklärenden Namen.

Strukturierte Programmierung auf Mikrocontrollern

>Dazu stelle ich mir vor, so viel wie möglich in einzelne spezielle
>Funktionenn zu stopfen um das main() zu entlasten und schlank zu halten.

Ist OK.

>Ist der Gedanke falsch, das main() ausschliesslich/vorwiegend für die
>Konfig (Ports, Var, etc.) und der Ansteuerung der einzelnen Funktionen
>vorzusehen?

Nein. main() ist eher wie ein Inhaltsverzeichnis eines Buches. Selbst 
die Initialisierung von einzelenen Komponenten kann man in Funktionen 
auslagern, auch wenn diese nur einmalig aufgerufen werden.

von Michael B. (laberkopp)


Lesenswert?

__Son´s B. schrieb:
> Macht es Sinn, diesen gesamten main()-Code, in eine Funktion SpgUeberw()
> auszulagern.

Nein.

Der Grundsatz sollte sein: Mache es so einfach wie möglich.

D.h. man macht nichts, was nicht fachlich begründet ist.

Häufig hört man als Begründung: "Ich habe das schon mal so gemacht, 
damit man es später leichter erweitern kann." Und meistens wird es dann 
nie erweitert. Und wenn es erweitert wird, passt es nicht dort hin. 
Daher ist zumindest jene 'Begründung' und der damit verbundene das 
eigentliche Problem verschleiernde Aufwand Unsinn.

Eine Begründung "Weil es übersichtlicher ist" kann gut sein, wenn aber 
jemand nachfragt, warum man das so gemacht hat, wurde es wohl nicht 
übersichtlicher, d.h. man lügt sich oft selbst etwas vor. Meist ist es 
am übersichtlichsten, wenn es so einfach wie möglich gemacht wurde.

Womit wir wieder beim ersten Satz wären. Die Informatik hat sogar klare 
Massstäbe: Ein Algorithmus ist dann gut, wenn man ihn nicht weiter 
vereinfachen kann, d.h. er arbeitet das Problem mit der minimal 
notwendigen Anzahl von Operationen ab. Das schliesst natürlich alles 
ein, was 'notwendig' ist, z.B. Fehlerbearbeitung, und kann auch längeren 
Code bedeuten der aber statistisch weniger lange läuft (quicksort statt 
bubblesort).

Ein Auslagern von Programmzeilen in eine Funktion, die nicht mehrmals 
aufgerufen wird (das wäre die fachliche Begründung) ist also Unsinn und 
macht das Programm weder effektiver noch übersichtlicher, es sei denn, 
die Funktion ist eine Standardfunktion wo man schon am Namen ablesen 
kann was sie tut, sich also die Funktion selbst zum Verständnis gar 
nicht mehr angucken muss.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Hab mal ein bischen am Code rumgefummelt, so finde ich es besser lesbar.
- mMn. ist Code lesbarer, wenn er komplett in englisch verfasst wird, 
die Mixtur ist schwerer zu lesen
- etwas kompakter gestalten, nicht unnütz viele Klammern
- doppelten code vermeiden
- Magic Numbers gegen #define SYM ersetzen
- irgendwie unschön, in einer StateMachine ein Delay zu verwenden.
  Besser: state = DecodeState(prevState); oder so, und die DelayMs() 
ausserhalb starten.

1
int main(void)
2
{
3
  DDRB |=  (1<<PB4) | (1<<PB2) | (1<<PB1) | (1<<PB0);   // Ausgang PB4=Batt_ON, PB2=low, PB1=mid, PB0=high
4
  DDRB &= ~(1<<PB5);                                    // Eingang PB5...Master_OFF wenn 1
5
6
  uint16_t  average;                                    // Mittelwert
7
  uint8_t   activeLed = 2;                              // aktueller LED-Zustand, 1...OberBereich, 2...MittlerBereich, 3...UnterBereich
8
  uint8_t   state     = 3;                              // StateMaschine-state
9
10
  ADC_Init();
11
12
  while(1) {
13
    average = ADCReadAverage(3, 100, 0);                // Mess-Mittelwert, (Eingangs-Kanal (3=ADC3), Mess-Durchläufe, Intervall [us])
14
15
    if(average >= MAX_RANGE && (activeLed==1 || activeLed==2  ) state = 1; // LED_high ... Oberer Bereich
16
    if(average >= MAX_RANGE &&  activeLed==3                  ) state = 2; // LED_high, von LED_low kommend
17
    if(average <  MAX_RANGE &&  average       > MIN_RANGE     ) state = 3; // LED_mid ... Mittlerer Bereich
18
    if(average <= MIN_RANGE && (activeLed==1                  ) state = 4; // LED_low, von LED_high kommend
19
    if(average <= MIN_RANGE && (activeLed==3 || activeLed==2  ) state = 5; // LED_low ... Unterer Bereich
20
21
    switch(state) {
22
      case 1:             // high
23
        activeLed = 1;    // "OberBereich"
24
        Batt(1);          // Batterieausgang ein
25
        break;
26
27
      case 2:             // LED_high, erst über LED_mid, von LED_low kommend
28
        SetLed(2);        // LED/mid ein, Rest aus
29
        DelayMs(500);     // Mindest-Wartezeit [ms]
30
        activeLed = 1;    // "OberBereich"
31
        Batt(1);          // Batterieausgang ein
32
        break;
33
34
      case 3:             // mid
35
        activeLed = 2;    // "MittlerBereich"
36
        DelayMs(500);     // Mindest-Wartezeit [ms]
37
        break;
38
39
      case 4:             // LED_low, erst über LED_mid, von LED_high kommend
40
        SetLed(2);        // LED/mid ein, Rest aus
41
        DelayMs(500);     // Mindest-Wartezeit [ms]
42
        activeLed = 3;    // "UnterBereich"
43
        Batt(0);          // Batterieausgang aus
44
        break;
45
46
      case 5:             // low
47
        activeLed = 3;    // "UnterBereich"
48
        Batt(0);          // Batterieausgang aus
49
        break;
50
    }
51
    
52
    SetLed(activeLed);
53
    DelayMs(100);       // [mSek], Wartezeit bis nächste Messwertauswertung
54
  }
55
}

: Bearbeitet durch User
von Sven S. (boldie)


Lesenswert?

Ich würde die Magic Numbers (1,2,3,4,5, was ist das?) raus machen und 
dort  eine enum oder konstanten nutzen. Dann kannste dir auch vieles der 
zum Teil doppelten Kommentare sparen.

Ansonsten, wenn du für später etwas lernen willst in dieser Hinsicht, 
lies mal Clean Code, ein empfehlenswertes Buch, vor allem wenn man mit 
anderen zusammen arbeiten möchte.

von Cyblord -. (cyblord)


Lesenswert?

Es gibt ein paar grundsätzliche Tipps:

1.) keinen Redundanten Code.
2.) Jede Funktion hat eine, genau EINE und nur EINE Aufgabe die man in 
einem kurzen Satz beschreiben kann.
3.) Komplexe Aufgaben in einfachere Teilaufgaben zu zerlegen ist DAS 
Grundkonzept jeder Programmierung und die Methode um überhaupt komplexe 
Aufgaben bewältigen zu können.
4.) Das ist nichts anderes als eine Methode zur Abstraktion (neben 
anderen). Abstrahiere über komplexe Aufgaben und behalte die Komplexität 
dort an einem Ort. Sie darf nicht ausbrechen ;-)
5.) Modulkonzept: Klare Schnittstellen definieren. Nichts geht daran 
vorbei.
6.) Lose Kopplung: So wenig Abhängigkeiten wie möglich. Jede 
Abhängigkeit von einer außenstehenden Ressource oder Konstruktion wird 
sich massiv negativ auf Erweiterungen und Veränderungen auswirken.

von Sebastian V. (sebi_s)


Lesenswert?

Michael B. schrieb:
> Ein Auslagern von Programmzeilen in eine Funktion, die nicht mehrmals
> aufgerufen wird (das wäre die fachliche Begründung) ist also Unsinn und
> macht das Programm weder effektiver noch übersichtlicher

Natürlich kann es dadurch übersichtlicher werden. Nicht gerade im 
gezeigten Beispiel wo alles nur eine Funktion ist aber wenn ich 100 
Zeilen Code habe und ich kann daraus 5 sinnvolle Funktionen (also nicht 
myFuncStep1, myFuncStep2, ...) mit jeweils 20 Zeilen Code machen, welche 
einfach nacheinander aufgerufen werden, dann mache ich das so.

: Bearbeitet durch User
von Cyblord -. (cyblord)


Lesenswert?

Sebastian V. schrieb:
> Michael B. schrieb:
>> Ein Auslagern von Programmzeilen in eine Funktion, die nicht mehrmals
>> aufgerufen wird (das wäre die fachliche Begründung) ist also Unsinn und
>> macht das Programm weder effektiver noch übersichtlicher
>
> Natürlich kann es dadurch übersichtlicher werden. Nicht gerade im
> gezeigten Beispiel wo alles nur eine Funktion ist aber wenn ich 100
> Zeilen Code habe und ich kann daraus 5 sinnvolle Funktionen mit jeweils
> 20 Zeilen Code machen, welche einfach nacheinander aufgerufen werden,
> dann mache ich das so.

Richtig. Jemand der so was behauptet hat einfach überhaupt keine Ahnung 
von Softwareentwicklung. Der programmiert halt was hin und freut sich 
wenns läuft. Wer einmal wirklich komplexe Programme gemacht hat, weiß um 
die Schwierigkeiten. Wer nur kleine Blinky Programme macht kann sich die 
Notwendigkeit von solchen softwaretechnischen Konstrukten eben nicht 
vorstellen.

Funktionen dienen eben nicht nur zum vermeiden von redundantem Code. Die 
bereits genannte Abstraktion ist das viel wichtigere.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?


von Michael B. (laberkopp)


Lesenswert?

Sebastian V. schrieb:
> Natürlich kann es dadurch übersichtlicher werden. Nicht gerade im
> gezeigten Beispiel wo alles nur eine Funktion ist aber wenn ich 100
> Zeilen Code habe und ich kann daraus 5 sinnvolle Funktionen (also nicht
> myFuncStep1, myFuncStep2, ...) mit jeweils 20 Zeilen Code machen, welche
> einfach nacheinander aufgerufen werden, dann mache ich das so.

Du machst das so. Übersichtlicher wird es dadurch nicht, denn neben den 
100 Zeilen Code muss er auch noch zusätzlich rausfinden, in welcher 
Reihenfolge die nun drankommen, und warum irgendein Depp die aufgeteilt 
hat denn niemand denkt sich daß das ganz ohne fachliche Begründung 
erfolgte, man rätselt also an deinen verqueren Code. Klar, DU rätselst 
nicht, aber alle anderen.

Sven S. schrieb:
> Ich würde die Magic Numbers (1,2,3,4,5, was ist das?) raus machen

Ich würde wohl vor allem den völlig falschen fachlichen Ansatz löschen, 
daß dort von "state machine" geredet wird und dann gar keine state 
machine benutzt wird. Der state wird ja jedesmal direkt vor dem 
switch/case neu berechnet.

Ich finde den Code maximal unübersichtlich weil so viele meiner Meinung 
nach nutzlose Dinge gemacht werden. So wie ich den code (bisher) 
verstehe, gibt es 3 LEDs je nach gemessener Akkuspannung (whrscheinlich 
rot, gelb, grün), die jede 100ms gemessen wird und woraufhin die 
passende LED eingeschaltet wird.
Die einzige Besonderheit ist eine Wartezeit von 500ms. Aber die wird 
idiotisch oft eingefügt:
Im Normalzustand 3 MIN_RANGE<average<MAX_RANGE (LED 2) wird nach 100ms 
Schleifendelay auch IMMER 500ms Zustandsdelay eingefügt, es wird also 
effektiv nur alle 600ms gemessen und auf Veränderungen reagiert. Ob er 
das wollte ?
Verändert sich die Spannung dann aus average<=MIN_RANGE oder 
MAX_RANGE<=average, dann wird sofort der Zustand eingekommen.
Verändert sich die Spannung aber von LED 1 oder LED 3 auf den 
entgegengesetzten Zustand, wird mindestens für 500ms+100ms die LED2 
aktiviert. Das geht einfacher zu formulieren:
1
// notwendige Initialisierungen
2
while(1)
3
{
4
    av=ADCReadAverage(3, 100, 0); 
5
    if(av>=MAX_RANGE)
6
    {
7
        if(led>1) led--;
8
    }
9
    else if(av<=MIN_RANGE)
10
    {
11
        if(led<3) led++;
12
    }
13
    else led=2;
14
    SetLED(led);
15
    if(led==1) Batt(1); else if(led==3) Batt(0);
16
    DelayMs(600);
17
}
Meiner Meinung nach ist das der ganze Code, und ich denke mir, er wollte 
eigentlich einen Delay von 500ms statt 600ms was man aber erst sieht, 
wenn man den aufgeräumten code sieht.
Auch ist die Frage, was passieren soll, wenn die Spannung innerhalb der 
500ms Wartezeit schon wieder aus dem Überschreitungsbereich herauskommt. 
Soll dann trotzdem die LED 1 oder 3 für 100ms blitzen und vor allem die 
Batt kurz ausgeschaltet bzw. eingeschaltet werden oder besser nicht ?

: Bearbeitet durch User
von Michael B. (laberkopp)


Lesenswert?

Falk B. schrieb:
> Michael Bertrandt (laberkopp) schrieb:
>                    ^^^^^^^^^
> Noch Fragen?

Keine Fragen, wer fachlich doof ist, versucht halt persönliche 
Beleidigungen.

von __Son´s B. (bersison)


Lesenswert?

Hi Leute!

Nocht Fragen >>> NEIN!
Sind ausgezeichnette Ansätze und Gedankenmodelle dabei --- VIELEN DANK!

Es gibt nicht nur DEN EINEN WEG, sondern viele Wege zu EINEM ZIEL.
"Jeder Jeck ist Anders" - somit sollte persönliche 
Diskriminierungen/Angriffe in einem qualifiziertem Forum keinen Platz 
haben - habe ich daher gerne überlesen...

von __Son´s B. (bersison)


Lesenswert?

__Son´s B. schrieb:
> Sind ausgezeichnette Ansätze und Gedankenmodelle dabei

Vor Allem, werde ich zusammengehörige Funktionen zukünftig, mit dem 
gleichen Index zu beginnen und deutlicher/ausführlicher zu Bezeichnen;

Bsp:
SpgUeberwach_ADWandel()
SpgUeberwach_Auswertung()
SpgUeberwach_Mittelwert()
SpgUeberwach_BereichZuordnung()
etc.

Somit ist klar erkennbar, warum es geht und vor allem, welche Funktionen 
zusammen gehören.

von Falk B. (falk)


Lesenswert?

@__Son´s Bersi__ (bersison)

>Somit ist klar erkennbar, warum es geht und vor allem, welche Funktionen
>zusammen gehören.

Eben, dann ist es fast schon C++ ;-)

Klasse.Methode();

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.