Forum: Compiler & IDEs Fehlerbehandlung_C++ auf µC


von goldeneyes1987 (Gast)


Lesenswert?

Hi Guys,

meine Frage gilt der Fehlerbehandlung in C++.
Ich weiss das ich Fehler mit den try and catch Befehlen abfangen und 
behandeln kann.
Jedoch wie geeignet ist dieses wenn das Programm auf einen µC laufen 
soll?
Ich mich da mal durchgelesen(google & friends), in einigen Artikeln 
wurde strikt davon abgeraten aber keine alternative geboten!

Habt ihr da schonmal was in diese Richtung gemacht oder gehört?

Ein Beispielcode ist hier aufgeführt, aber diese Lösung gefällt mir 
nicht!!!
1
bool Board::addRcChannels(uint8_t numbers_of_ch){
2
3
  if(numbers_of_ch > RC_PIN_MAX) return false; // error detedected
4
  else
5
  {
6
    if(RC_PORT == PORTK) DDRK = 0; // define PORTK as a input //to remove(bad here???)
7
8
    for(uint8_t i = 0; i < numbers_of_ch; i++)
9
    {
10
     RC_PORT |= (1<<rc_Pins[i]);    // enable Pull-ups
11
     PIN_CHANGE_MASK |= (1<<rc_Pins[i]);    // mask pin-change-interruptregister
12
     PC_ICR |= PC_ICRVect;      // enable pin-change_interrupt
13
    }
14
    return true;
15
  }
16
}



Gruß
GoldenEyes

von Simon H. (simi)


Lesenswert?

Eine Abwandlung von dem von Dir genannten Beispiel ist, dass 
grundsätzlich jede Funktion einen Error-Code zurückgibt (typedef auf 
int). Dieser wird lokal gespeichert. Initialisierung auf 'ok'.
Jede Funktion wird nur dann aufgerufen, wenn der lokale Fehlercode noch 
'ok' entspricht.
Wenn eine Funktion mit einem von ihr aufgerufenen Fehlercode nichts 
anfangen kann, reicht sie ihn halt einfach weiter hinauf. Ansonsten 
unternimmt sie Massnahmen und setzt einen ensprechend neuen Fehlercode.


Nachteile:
-Jeder Funktionsaufruf muss in einem if-Block stattfinden, der prüft, ob 
der (jeweils lokal gespeicherte) Fehlercode noch "ok" entspricht. -> 
Zeilenzahl quasi verdreifacht.

-Werterückgabe nur noch durch out parameter.

Man kann aber recht weitführendes error handling damit machen.

Gruäss
Simon

von goldeneyes1987 (Gast)


Lesenswert?

Danke schonmal für die schnelle Antwort!

Ich will halt noch die Fehler codieren damit ich weiss wo genau was 
schief gelaufen ist. Da genügt kein "nOK", da ich ja nicht weiss wer das 
gesetzt hat?

Gruß
GoldenEyes

von Klaus W. (mfgkw)


Lesenswert?

Erst musst du dich durchringen, was du unter uC verstehst.
Auf einem AVR bis zu einem kleinen ARM kannst du Ausnahmen leider 
vergessen, ab da irgendwo lohnt es sich wieder darüber nachzudenken.

von goldeneyes1987 (Gast)


Lesenswert?

Ja vorerst soll das ganze auf einen AVR laufen aber schon vorstellbar 
später auf einem "großen" ARM laufen soll!

von goldeneyes1987 (Gast)


Lesenswert?

Eine weitere Idee war es z.B. so zu machen:
1
// in main.cpp
2
//  hier prüfe ich z.B. ob ein Pin nicht schon besetzt ist( mit "anything" abk.)
3
if (anything) error.addError("Pin ist schon besetzt, Fehler in main.cpp")
4
else
5
       do somthing
6
7
// in Error.h
8
9
class Error{
10
11
  private: 
12
       String list[MaxError];
13
  public:
14
      void addError(String erStr);
15
16
}
17
18
Error error;

Was denkt Ihr darüber?

Gruß
Goldeneyes

von Klaus W. (mfgkw)


Lesenswert?

Alles, was mit dynamischer Speicherverwaltung arbeitet, wird auf einem 
AVR o.ä. schnell knapp.
Im Zweifelsfall fährst du besser damit, zumindest in einer Debugvariante 
Trace-Ausgaben z.B. über RS232 in die Welt zu schicken und auf dem uC 
wieder zu vergessen.

Wo willst du denn sonst das alles sammeln? In den Lymphdrüsen?

von Klaus F. (kfalser)


Lesenswert?

goldeneyes1987 schrieb:
> meine Frage gilt der Fehlerbehandlung in C++.
> Ich weiss das ich Fehler mit den try and catch Befehlen abfangen und
> behandeln kann.
> Jedoch wie geeignet ist dieses wenn das Programm auf einen µC laufen
> soll?

Die Fehlerbehandlung auf einem (kleinen =? ) µC hat andere Anforderungen 
als auf einem PC und ich denke viele machen den Fehler die beim PC 
gewünschte und erzielbare Flexibilität auf den µC bringen zu wollen.
Dein Beispiel (wenn ich den Kontext richtig rekonstuiere) ist ein 
solcher Fall. Warum das Programm so schreiben, dass ich Kanäle zur 
Laufzeit beliebig dazufügen kann? Die Resourcen des µC sind begrenzt, 
deshalb überlege ich mir zuerst vieviele Kanäle maximal gebraucht werden 
oder möglich sind, und dann lege ich sie alle statisch an.
Das Programm verwendet dann zwar nicht alle, und das muß ich verwalten, 
aber ich habe keine Möglichkeit beim Anlegen Fehler zu machen. Nachteile 
habe ich auch keine, weil der Speicher des µC ist sowieso da und mehr 
als im statischen Fall bekomme ich sowieso nicht. Eher vielleicht 
weniger, weil die Verwaltung der dynamische Objekte etwas brauchen wird.

von Simon H. (simi)


Lesenswert?

goldeneyes1987 schrieb:
> Ich will halt noch die Fehler codieren damit ich weiss wo genau was
> schief gelaufen ist. Da genügt kein "nOK", da ich ja nicht weiss wer das
> gesetzt hat?

Ich habe mich vielleicht unklar ausgedrückt: Fehlercodes gäbe es in 
meinem Vorschlag natürlich viele. Und Du kannst dann auch reincodieren, 
woher der Fehler gekommen ist. z.B. 8 Bit Package, 16 Bit Klasse, 8 Bit 
Fehlercode.

Natürlich muss man das recht gründlich machen, aber so kann man, wenn 
die "oberste" Routine einen Fehler ausspuckt, genau sagen, wo er 
entstanden ist und warum.

Ausser natürlich, wie erwähnt, eine Routine weiter "oben" kennt den 
Fehler und handled ihn.

Ansonsten wird der gesamte weitere Verlauf des Programms unterbrochen 
(kein Aufruf mehr, weil eben der Fehlercode nicht mehr 'ok' ist, und 
zuoberst kann dann ein BSoD gezeichnet werden mit dem Code. Dann ein 
kleines VB-Skript, das ihn in Klartext übersezt: Package A, Klasse B, 
Fehler C.

von goldeneyes1987 (Gast)


Lesenswert?

Ich denke kfalser hat mich falsch verstanden!

Die überprüfung findet nur in der Initialisierungsphase statt!!
Folgende Anmerkung:
Die Module sollen zwar zusammen wervendet werden, jedoch sollen diese 
auch getrennt benutzt werden könne (natürlich. nur in bestimmten 
konstelationen).
Die gesamt Software(alle Module) sollen es ermöglichen einen Drohnen zu 
fliegen.
Aber es soll auch z.B. möglich sein nur die RC zu lesen und mit den 
Signalen etwas anders zu machen.
Die Klasse Board verwaltet die Hardware und vergibt freie Pins je nach 
µC.
Wenn jemand jetzt 10 Pins anfordert aber gerade der verwendete µC nur 8 
hat-> Fehlermeldung!
Die Person die Pins muss sich nicht wissen wieviele Pins der µC hat!


Evtl. ist jetzt klarer geworden wass ich eigentlich vorhabe!

Gruß
GoldenEyes

von Mark B. (markbrandis)


Lesenswert?

Klaus Wachtler schrieb:
> Wo willst du denn sonst das alles sammeln? In den Lymphdrüsen?

Hm, vielleicht (rotierende) Logfiles auf einer SD-Karte?
Haut vermutlich nicht mehr wirklich hin, wenn es viele Fehler gibt, weil 
der µC dann wohl mehr Rechenzeit mit dem File-I/O verbringt als mit 
allem anderen. Aber bei sporadischen Fehlern könnte ich mir das durchaus 
noch vorstellen.

von Karl H. (kbuchegg)


Lesenswert?

goldeneyes1987 schrieb:

> Die überprüfung findet nur in der Initialisierungsphase statt!!

Das ist letzten Endes egal.
Der springende Punkt ist, dass das alles Speicher benötigt. Auf einem 
größeren ARM ist das kein Problem, auf einem kleinen AVR aber schon.

> Die Module sollen zwar zusammen wervendet werden, jedoch sollen diese
> auch getrennt benutzt werden könne (natürlich. nur in bestimmten
> konstelationen).

Da würde ich mir überlegen, ob ich nicht einen Konfigurator machen kann, 
der auf dem PC läuft.
D.h. auf dem PC stellt mir ein spezielles Programm aus Codebausteinen 
die tatsächlich zu benutzende Konfigration in Form eines C++ Programmes 
zusammen. Dieses wird dann compiliert und in den µC gebrannt.

Wie Klaus Falser weiter oben schon schrieb: Auf einem kleinen µC kann 
man es mit der Flexibilität ganz schnell übertreiben. Und dann ist 
Schicht im Schacht.

von Klaus W. (mfgkw)


Lesenswert?

Wenn man SD-Karte o.ä. hat, ist es sicher sinnvoll, da Logs abzulegen.

Eine Überlegung wäre, das von der restlichen Funktion zu trennen:

1. Ein uC macht wie gehabt die Arbeit, schreibt Logs über RS232 oder was 
man halt als Schnittstelle hat.

2. Ein zweiter nimmt die, und versucht alles auf SD o.ä. zu speichern.
Stattdessen kann man auch einen PC o.ä. anklemmen, und dort auswerten 
oder speichern.

Das bringt mehr Flexibilität beim Entwickeln, in der Release-Version 
lässt man dann den zweiten einfach weg.

Was zuviel ist, wird halt notfalls verworfen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Das ist letzten Endes egal.
> Der springende Punkt ist, dass das alles Speicher benötigt. Auf einem
> größeren ARM ist das kein Problem, auf einem kleinen AVR aber schon.

Was ich vergessen hab:
Gerade auf einem kleinen µC hat man dann auch noch das Problem, dass man 
mit zuviel Flexibilität den Compiler daran hindert, Optimierungen 
einzusetzen.

Wir alle hätten zb auf einem AVR gerne ein LCD Modul, das man zur 
Laufzeit auf eine bestimmte Pinkonfiguration konfigurieren kann. Nur hat 
das extreme Nachteile, weil der Compiler dann nicht mehr in der Lage 
ist, die speziellen Einzelbitzugriffs-Befehle an den Ports zu benutzen, 
sondern tatsächlich bei jedem
    PORTx |= ( 1 << Lcd.PinE );
die Bitverschiebung im Code machen zu müssen und durch einen kompletten 
Read-Modify-Write Zyklus vom PORTx durchmuss. Gegenüber der Lösung, die 
der Optimizer bei konstanter Konfiguration erzielen kann, und die in 
einem einzigen Assembler Befehl mündet, ist das eine Verschlechterung, 
die keiner akzeptieren will. (Ganz zu schwiegen von all den Problemen, 
die dadurch entstehen werden, wenn diese Anweisung eben nicht zu einer 
einzigen atomaren Anweisung optimiert wird)

von goldeneyes1987 (Gast)


Lesenswert?

Hi,

als ich denke meine Lösung soll eher dahingehend aussehen wie das Klaus 
beschrieben hatt. Fehler werden registriert und "irgendwo" 
hingeschrieben.
Wenn der Benutzer merkt etwas geht nicht dann kann er "irgendwo" 
hinschauen und nachlesen wass evtl. die Ursache sein kann!



Oder habe ich immer noch nen denkfehler??

Gruß

von Nico S. (nico22)


Lesenswert?

Ja, wo ist dieses "Irgendwo"?

von goldeneyes1987 (Gast)


Lesenswert?

Nico Sch. schrieb:
> Ja, wo ist dieses "Irgendwo"?

"Irgendwo" kann auf sein auf einen Flash, SD-Karte oder driekt auf den 
PC über ein Funkmodul(XBee/WLAN);

Natürlich soll dafür ein zweiter Controller verwendet werden!

von Simon H. (simi)


Lesenswert?

Ich verstehe immernoch nicht ganz, warum Du es so kompliziert machen 
willst.

Dein Prog, zumal es in einem uC hockt, hat ja bestimmt eine Main-Loop. 
Und in der kannst Du jedesmal, wenn irgendwo im Code ein Fehler passiert 
ist, in aller Ruhe diesen Fehler ablegen, wohin Du willst. Mit genauer 
Beschreibung, was wo schiefgelaufen ist.

Durch geeignetes Platzieren von "Handlern", die einen Fehlercode, z.B. 
im Sinne von: 'IP-Stack, UDP-Packet, Invalid Port' übersetzt in 
'RTP-Stack, RTP-Stream-Handler, Sending UDP-Packet failed' übersetzt, 
kannst Du einen für Dich geeigneten Tradeoff zwischen Granularität und 
Lokalisierbarkeit erreichen. Und wenn Du im RTP-Stream-Handler schon den 
Fehlercode übersetzt, kannst Du ja auch gleich einen Log-Eintrag machen: 
Sending UDP-Packet failed, ErrorCode 0xf3746234, wobei 0xf3746234 in 
Prosa übersetzt heisst: 'IP-Stack, UDP-Packet, Invalid Port'

Ich könnte mir auch vorstellen, dafür eine Art Stack zu machen. Immer, 
wenn eine Routine einen Fehler zurückgibt, schiebt sie diesen zusätzlich 
auf diesen Stack. Die übergeordnete Routine interpretiert diesen Fehler, 
wenn sie kann, verändert ihn (s.o.) und legt diesen Fehler wiederum auf 
den Stack.

Die Main-Loop sieht nun: Ah, Fehler, --> schreibt den gesamten 
Fehlerstack raus, und man kann den Feheler vom Ursprung her bis nach 
ganz oben tracen (und somit, bei geschickter Platzierung dieser Handler 
darauf schliessen, was zum Fehler geführt hat).

Nur so eine Idee... hab's selber noch nie probiert

Gruäss
Simon

von Udo S. (urschmitt)


Lesenswert?

In vielen Geräten ist das irgendwo einfach eine Reihe LEDs die den Code 
anzeigen (Sofern noch ein paar Port Bits frei sind)

von micha (Gast)


Lesenswert?

Wenn es statisch ist kann man auch viele Fehlerbehandlungen mit "#ifdef" 
und "assert"-macros machen und ggfs. durch den Emulator hauen.
1
void Board::addRcChannels(uint8_t numbers_of_ch){
2
3
  assert(numbers_of_ch > RC_PIN_MAX); // error detedected
4
5
  for(uint8_t i = 0; i < numbers_of_ch; i++)
6
    {
7
     RC_PORT |= (1<<rc_Pins[i]);    // enable Pull-ups
8
     PIN_CHANGE_MASK |= (1<<rc_Pins[i]);    // mask pin-change-interruptregister
9
     PC_ICR |= PC_ICRVect;      // enable pin-change_interrupt
10
    }
11
  }
12
}

von micha (Gast)


Lesenswert?

sollte  natürlich
1
  assert(numbers_of_ch <= RC_PIN_MAX); // error detedected

sein...

von goldeneyes1987 (Gast)


Lesenswert?

Ich denke ich werde vorerst dieses mit Hilfe einer Codiertabelle machen!
Am Ende der Init. wird ein String (zahlen???) in die LOG geschrieben.
Anhand der Tabelle kann dann auf den Fehler zurückgeschlossen werden.
So ähnlich wird das im Automobilbereich gelöst
->Fehlercods kennt bestimmt jeder :-(

Ich denke so sollte ich einfach zur einer "akzeptablen" Lösung kommen!??

Gruß
GoldenEyes

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.