Forum: Compiler & IDEs die Warteschleife und MISRA


von Bauform B. (bauformb)


Lesenswert?

hallo c-hater und Freunde,

wir warten, bis die 24V halbwegs stabil sind. Das könnte doch so 
aussehen:
1
   for (int vs_ok = 0; vs_ok < 16; vs_ok++) {
2
      sleep_ms (16);
3
      if (adc_read_24V () < 8192) {
4
         vs_ok = 0;
5
      }
6
   }
aber MISRA erlaubt das nicht. Noch dazu mit so einer 
Pauschal-Begründung:
1
Using a restricted form of loop makes code easier
2
to review and to analyse.
Steckt da mehr dahinter?
Ist sowas wirklich leserlicher?
1
   int vs_ok = 0;
2
3
   do {
4
      sleep_ms (16);
5
      if (adc_read_24V () > 8192) {
6
         vs_ok += 1;
7
      } else {
8
         vs_ok = 0;
9
      }
10
   } while (vs_ok < 16);

von Homer (Gast)


Lesenswert?

Hi,

die erste Variante ist deswegen unglücklich (und von MISRA angezählt), 
da es kein guter Stil ist, die Zählvariable einer for Schleife im Rumpf 
nochmal schreibend zu verändern. Das ganze Konstrukt mit dem 
busy-waiting auf ein externes Ereignis (Spannungslevel) ist insgesamt 
eher schlecht gewählt, da es - auch so wie es geteilt ist - sinnlos 
Rechenzeit verbraucht. Das Ganze sollte man eher von der Anwending 
entkoppeln und in einen Treiber auslagern. Der könnte das busy-weiting 
dann über einen Zustandsautomaten erledigen und somit rechenzeitneutral 
bleiben. Rückmeldung an die Anwendung erfolgt dann über den Zustand des 
Automaten.

Grüsse
Frank

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Bauform B. schrieb:
> Using a restricted form of loop makes code easier
> to review and to analyse.
Lese mal den gesamten Absatz:

Rule 14.2 - In automatically generated code...
- 
https://www.misra.org.uk/app/uploads/2021/06/MISRA-C-2012-Permits-First-Edition.pdf

Wobei deine Gebilde meiner Meinung nach schon arg an code obfuscation 
grenzen. Mit deinem vs_ok = 0; baust du ja quasi eine Endlosschleife und 
führst die for-schleife ad absurdum. Von daher ist die zweite Version 
schon ein Stück besser. Da kommt man eher darauf das, dass gar keine 
Schleife mit 16 Durchläufen ist so wie es die erste Version suggeriert.

Bei so Gebilden wäre ein vorangestellter Kommentar mit "Die Spannung 
muss 16 mal hintereinander größer als x sein damit die Endlosschleife 
verlassen wird" doch ganz hilfreich:-)

von Bauform B. (bauformb)


Lesenswert?

Homer schrieb:
> Das ganze Konstrukt mit dem busy-waiting auf ein externes Ereignis
> (Spannungslevel) ist insgesamt eher schlecht gewählt

so allgemein ist das natürlich richtig, aber in diesem speziellen Fall 
darf das Programm nichts tun, solange die Spannung nicht ausreicht.

Trotzdem finde ich die for-Variante viel schöner!

von Programmierer (Gast)


Lesenswert?

Bauform B. schrieb:
> Ist sowas wirklich leserlicher?

Bei MISRA geht es nicht darum dass irgendwas leserlich wird, im 
Gegenteil, MISRA macht vieles unübersichtlich. Bei MISRA geht es nur 
darum, einen Satz völlig willkürlich gewählte Regeln zu erfüllen, damit 
man dann sagen kann, der Code sei "konform". Was zwar nichts aussagt 
außer dass niemand jemals wieder diesen Code anfassen will, aber 
irgendwie für gutes Karma wichtig ist.

von Homer (Gast)


Lesenswert?

wäre ja mit ausgelagertem Automaten kein Problem: Die Anwendung schaut 
ob der Automat den Zustand "Spannung erreicht" erreicht hat (was erst 
nach entsprechend entprellter Überschreitung der Schwelle erfolgt 
(Hysterese wäre hier vielleicht auch sinnvoll)), dann geht es erst 
weiter. Damit kann dann auch der Überwachungsautomat in einer beliebigen 
von der Anwendung unabhängigen Zeitscheibe laufen und sogar während der 
Anwendungslebenszeit die Spannung weiter überwachen, falls auch ein 
Wiederunterschreiten problematisch wäre...

von Bauform B. (bauformb)


Lesenswert?

Irgend W. schrieb:
> Wobei deine Gebilde meiner Meinung nach schon arg an code obfuscation
> grenzen. Mit deinem vs_ok = 0; baust du ja quasi eine Endlosschleife und
> führst die for-schleife ad absurdum. Von daher ist die zweite Version
> schon ein Stück besser. Da kommt man eher darauf das, dass gar keine
> Schleife mit 16 Durchläufen ist so wie es die erste Version suggeriert.

Das ist ein Argument, danke. Ich glaube schon, dass in den meisten 
Regeln so ein Körnchen Wahrheit versteckt ist.

Und danke für den Link!


Programmierer schrieb:
> MISRA (...) irgendwie für gutes Karma wichtig ist.

na, wenn das kein guter Grund ist :)

von MaWin (Gast)


Lesenswert?

Bauform B. schrieb:
> aber MISRA erlaubt das nicht.

Natürlich nicht.

Weil es grober Schrott wäre.

Da misst man nie 24V, weil es nur 23 sind oder die Referenzspannung 
schwankt, und dein (Steuergerät?) bleibt noch in der 
Initialisierungsphase hängen, sogar wenn nur alle 100ms ein kurzer 
Einbruch passiert, ohne dem Benutzer zu sagen, warum, und ohne eine 
sinnvolle Aktion anzuleiern.

Sagte ich schon, das ist Schrott.

Man erwartet z.B. dass die 24V in 0.1 Sekunde anliegen, erlaubt 0.25 
Sekunden durch einen Zähler oder Timerabfrage und macht dann auch bei 
13V weiter um Fehlermeldung auszugeben.

Oder man startet gleich und misst dauernd und wenn die 24V mal zu wenig 
sind, speichert man das in einem Fehlerflag das dann (auf Anforderung?) 
gesendet wird.

Zudem ist deine Schleife übel gestaltet: erst erweckt sie den Eindruck, 
nach 16 Durchläufen zu terminieren, dann mogelt sie sich doch zur 
Endlosschleife. Solche Programmierhansel würde ich rausschmeissen. Man 
manipuliert Schleifenzähler nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> hallo c-hater und Freunde,
>
> wir warten, bis die 24V halbwegs stabil sind. Das könnte doch so
> aussehen:
>
1
   for (int vs_ok = 0; vs_ok < 16; vs_ok++) {
2
>       sleep_ms (16);
3
>       if (adc_read_24V () < 8192) {
4
>          vs_ok = 0;
5
>       }
6
>    }

Was MISRA hier natürlich leider nicht anmerkt, sind andere Dinge:

* Was bedeutet adc_read_24V() ? Der ADC liest immer 24V?
* Was ist 8192?
* Der Name vs_ok suggeriert ein Prädikat: true/false, warum dann DT int?
* Und natürlich eine potentiell blockierende Operation (wurde schon 
genannt).

> Ist sowas wirklich leserlicher?
>
1
   int vs_ok = 0;
2
> 
3
>    do {
4
>       sleep_ms (16);
5
>       if (adc_read_24V () > 8192) {
6
>          vs_ok += 1;
7
>       } else {
8
>          vs_ok = 0;
9
>       }
10
>    } while (vs_ok < 16);

Nein, weil die Schleifentransformation, die Du gemacht hast formell 
falsch ist: die Negation der Bedingung ist falsch (macht in der Praxis 
aber wohl weinig Unterschied ...). Ansonsten treffen weiterhin alle 
Probleme von oben zu, und zudem erweiterst Du unnötig den Scope von 
vs_ok.

Insgesamt liefert MISRA hier (wie auch sonst sehr häufig), keinen 
Beitrag zur Verbesserung. Die wirklich wichtigen Dinge werden von MISRA 
sehr oft nicht erfasst, weil sie oft in der unklaren Semantik von 
Konstrukten liegt. Wie in diesem Beispiel.

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> * Was ist 8192?

Ein Versuch, ein #define zu vermeiden ;) Ist MIN_STARTUP_VOLTAGE 
wirklich besser? Damit müsste ich erst irgendwo suchen, wie viel Volt 
das sind. Diese Konstante wird ja garantiert nur hier gebraucht.

> * Der Name vs_ok suggeriert ein Prädikat: true/false, warum dann DT int?

Einverstanden, aber der Name ist falsch, int passt schon.

> * Und natürlich eine potentiell blockierende Operation

Nicht nur potentiell, die muss hier blockieren. Solange der Stecker 
nicht in der Steckdose steckt, läuft die Kiste ja auch nicht. Und mit zu 
niedrigen 24V eben genauso wenig. Realistischer ist natürlich eine 
Schwelle oberhalb von 20V, drunter darf man z.B. kein Relais 
einschalten.

> und zudem erweiterst Du unnötig den Scope von vs_ok.

Tja, was soll ich machen, wenn ich kein for benutzen darf?
Also gut, neuer Versuch.
1
uint32_t t0;
2
3
t0 = uptime_ms;
4
for (;;) {
5
   if (adc_read_mV (VS_24V) < 21000) {
6
      t0 = uptime_ms;
7
   } else {
8
      if ((uptime_ms - t0) > 128) { break; }
9
   }
10
}

von Bauform B. (bauformb)


Lesenswert?

MaWin schrieb:
> Da misst man nie 24V, weil es nur 23 sind oder die Referenzspannung
> schwankt, und dein (Steuergerät?) bleibt noch in der
> Initialisierungsphase hängen, sogar wenn nur alle 100ms ein kurzer
> Einbruch passiert

Ja, das ist Absicht. Lieber keine Funktion als eine halbe.

> ohne dem Benutzer zu sagen, warum

Immerhin leuchtet eine rote LED, mehr geht leider nicht.

> und ohne eine sinnvolle Aktion anzuleiern.

Ich kann da keine Ersatzteile bestellen weil ich kein Internet hab ;)

MaWin schrieb:
> Man manipuliert Schleifenzähler nicht.

Das sehe ich ja ein, das ist ja der Grund meines Besuchs.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Wilhelm M. schrieb:
>> * Was ist 8192?
>
> Ein Versuch, ein #define zu vermeiden ;) Ist MIN_STARTUP_VOLTAGE
> wirklich besser?

Bei reinem C ist das schon mal besser.
Oder eine Umrechnungsfunktion: volt_to_raw(24).
Oder die Goodies von C++ nutzen: 24_V

>> * Der Name vs_ok suggeriert ein Prädikat: true/false, warum dann DT int?
>
> Einverstanden, aber der Name ist falsch, int passt schon.

Warum soll ein DT, der ein Anzahl repräsentiert negativ werden können?

>> * Und natürlich eine potentiell blockierende Operation
>
> Nicht nur potentiell, die muss hier blockieren.

Potentiell blockierende Operation ist eine Operation die unendlich lange 
blockieren kann, wenn das zugehörige Ereignis nicht eintritt. Genau das 
ist hier der Fall.

> Solange der Stecker
> nicht in der Steckdose steckt, läuft die Kiste ja auch nicht. Und mit zu
> niedrigen 24V eben genauso wenig. Realistischer ist natürlich eine
> Schwelle oberhalb von 20V, drunter darf man z.B. kein Relais
> einschalten.

Das ist Mist: der Anwender weiß nicht, warum die Kiste nicht läuft.

>
>> und zudem erweiterst Du unnötig den Scope von vs_ok.
>
> Tja, was soll ich machen, wenn ich kein for benutzen darf?
> Also gut, neuer Versuch.
1
uint32_t t0;
2
> 
3
> t0 = uptime_ms;
4
> for (;;) {
5
>    if (adc_read_mV (VS_24V) < 21000) {
6
>       t0 = uptime_ms;
7
>    } else {
8
>       if ((uptime_ms - t0) > 128) { break; }
9
>    }
10
> }

Wieder voll von magischen Konstanten und Variablen im umgebenden Scope. 
Das ist noch schlechter.

Ich denke mal, Dein Gerät kennt mehrere Globalzustände: Startup, Init, 
Run, Error, ...

Dann gehört das ganze in eine StateMachine, und dann ist das Verharren 
im "Init"-Zustand auch wesentlich klarer.

von Thorsten S. (thosch)


Lesenswert?

Bauform B. schrieb:
> Ein Versuch, ein #define zu vermeiden ;) Ist MIN_STARTUP_VOLTAGE
> wirklich besser?
Natürlich!
magic numbers im code sind immer Mist.
Ich würde genauso den Delay-Wert und den Schleifen-Endwert per #define 
festlegen.

> Damit müsste ich erst irgendwo suchen, wie viel Volt das sind.
Das ist kein Argument, weil jede auch nur halbwegs gescheite 
Entwicklungsumgebung dir den Wert eines #define beim drüberhovern mit 
dem Mauszeiger anzeigt.

Das ließe sich sogar noch wartbarer schreiben, wenn du nicht einfach 
8192 ins #define OPERATING_THRESHOLD_24V_ADC reinschreibst, sondern den 
Wert aus dem gewünschten Spannungs-Schwellwert und dem LSB-Spannungswert 
des ADCs (natürlich als weiteres #define festgelegt) ausrechnen läßt.

Wenn sich dann z.B. die Vref für den ADC mal ändern sollte, mußt du 
nicht alle magic numbers für ADC-Spannungswerte neu berechnen, sondern 
paßt nur das eine #define für den ADC-LSB-Wert an.

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Das ist Mist: der Anwender weiß nicht, warum die Kiste nicht läuft.

Natürlich, die Natur ist grausam, besonders, wenn Hardware nicht richtig 
funktioniert.

>> Ein Versuch, ein #define zu vermeiden ;) Ist MIN_STARTUP_VOLTAGE
>> wirklich besser?
>
> Bei reinem C ist das schon mal besser.
> Oder eine Umrechnungsfunktion: volt_to_raw(24).

Die Umrechnung steckt ja in adc_read_24V() bzw. jetzt umbenannt in 
adc_read_mV(int channel). Solche Funktionen liefern die Daten natürlich 
in mV. Insofern ist 8192 (jetzt 21000) garnicht soo magisch. Man könnte 
das Programm sogar auf Papier lesen ohne Umblättern zu müssen ;)

> Wieder voll von magischen Konstanten und Variablen im umgebenden Scope.
> Das ist noch schlechter.

Wie wäre es, wenn ich die Schleife in eine Funktion packe, so 
wait_for_24V()? Anders wird das Scope-Problem kaum zu lösen sein.


Thorsten S. schrieb:
> Das ließe sich sogar noch wartbarer schreiben, wenn du nicht einfach
> 8192 ins #define OPERATING_THRESHOLD_24V_ADC reinschreibst, sondern den
> Wert aus dem gewünschten Spannungs-Schwellwert und dem LSB-Spannungswert
> des ADCs (natürlich als weiteres #define festgelegt) ausrechnen läßt.

Wie gesagt, die adc_read_ Funktionen liefern mV.

> Wenn sich dann z.B. die Vref für den ADC mal ändern sollte, mußt du
> nicht alle magic numbers für ADC-Spannungswerte neu berechnen, sondern
> paßt nur das eine #define für den ADC-LSB-Wert an.

Und das gehört ins adc-Modul. Den Benutzer der adc-Funktionen geht das 
garnichts an.

von Peter D. (peda)


Lesenswert?

Will man professionell eine Spannung überwachen, dann prüft man, ob 
Standardabweichung und Mittelwert innerhalb eines festgelegten 
Toleranzfensters sind. Nur dann kann man feststellen, ob die Messung 
glaubwürdig ist.
Weiterhin ist ein Timeout sinnvoll, um eine Fehlermeldung auszugeben. 
Einfach nur endlos zu warten, läßt den Anwender ratlos dreinblicken.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Die Umrechnung steckt ja in adc_read_24V() bzw. jetzt umbenannt in
> adc_read_mV(int channel).

Ich hoffe, diese Funktion weiß mit adc_read_mV(-42) umzugehen ;-)

> Solche Funktionen liefern die Daten natürlich
> in mV. Insofern ist 8192 (jetzt 21000) garnicht soo magisch.

Gut, dann hast Du ja schonmal einen Ratschlag übernommen ;-)

>> Wieder voll von magischen Konstanten und Variablen im umgebenden Scope.
>> Das ist noch schlechter.
>
> Wie wäre es, wenn ich die Schleife in eine Funktion packe, so
> wait_for_24V()? Anders wird das Scope-Problem kaum zu lösen sein.

Congrats: Du hast es erkannt.
Man könnte es auch eleganter machen, aber dafür hast Du mit reinem C die 
falsche Sprache gewählt. Wobei Du sicher auch nur zum Nutzen der kleinen 
(großen) Goodies auch C++ einsetzen könntest (etwa hier: ein Closure).

Und dann denke über den expliziten Zustandsautomaten für die 
Globalzustände nach. Das macht ggf. vieles klarer. Und das wird sicher 
nicht der einzige Zustandsautomat sein ...

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Ich hoffe, diese Funktion weiß mit adc_read_mV(-42) umzugehen ;-)

Dagegen gibt es assert() oder return -1; dann muss eben der Aufrufer 
damit umgehen.

> Und dann denke über den expliziten Zustandsautomaten für die
> Globalzustände nach.

Ich hätte einen mit knapp 1000 Zeilen im Angebot, aber damit fangen wir 
jetzt lieber nicht an ;) Hier passt das irgendwie nicht so richtig. Der 
Ablauf so total linear, auf Zustand n kann nur n+1 folgen und zurück 
geht es nur über Reset. Ja, auch aus dieser Endlosschleife (außer, man 
spendiert mehr Volts).

Jedenfalls hab' ich was gelernt, danke dafür!

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Wilhelm M. schrieb:
>> Ich hoffe, diese Funktion weiß mit adc_read_mV(-42) umzugehen ;-)
>
> Dagegen gibt es assert()

Da bist Du einer der wenigen (neben mir), die das so sehen. Du kommst 
allerdings mit weniger Vorbedingungen aus, wenn Du gleich deinen DT mit 
einem besser eingeschränkten Wertebereich nimmt.

> oder return -1; dann muss eben der Aufrufer
> damit umgehen.

Da schießt Du Dir im obigen Code allerdings auch selbst ins Knie, denn 
das wird dort nicht behandelt.

>> Und dann denke über den expliziten Zustandsautomaten für die
>> Globalzustände nach.
>
> Ich hätte einen mit knapp 1000 Zeilen im Angebot, aber damit fangen wir
> jetzt lieber nicht an ;) Hier passt das irgendwie nicht so richtig. Der
> Ablauf so total linear, auf Zustand n kann nur n+1 folgen

Auch dafür sind SM wunderbar, und Du könntest für den Anwender noch eine 
LED anschalten, ohne das der Code noch unverständlicher wird.
und zurück

> geht es nur über Reset.
> Ja, auch aus dieser Endlosschleife (außer, man
> spendiert mehr Volts).

Mit mehr Spannung ist dass dann wohl eher ein unumkehrbarer Reset für 
immer ;-)

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Du kommst allerdings mit weniger Vorbedingungen aus, wenn Du
> gleich deinen DT mit einem besser eingeschränkten Wertebereich nimmt.

Traditionell gibt es für die Kanalnummern ein typedef enum. Das spielt 
mit einem switch schon ganz gut zusammen, nur beim Funktionsaufruf muss 
C (oder doch wieder ich?) noch üben.

>> oder return -1; dann muss eben der Aufrufer damit umgehen.
> Da schießt Du Dir im obigen Code allerdings auch selbst ins Knie, denn
> das wird dort nicht behandelt.

Manchmal ergibt sich das ganz natürlich: -1mV ist kleiner als 21V :) Da 
ist C wieder im Vorteil, eine richtige Sprache würde so eine Mischung 
aus mV und Status garnicht erlauben.

> Du könntest für den Anwender noch eine LED anschalten

Wenigstens das mache ich schon. Aber jetzt, wo du es sagst: 
normalerweise sorgt die Hardware für das passende Licht aus der LED, 
warum ging das auf dieser Platine eigentlich nicht?

> Mit mehr Spannung ist dass dann wohl eher ein unumkehrbarer Reset für
> immer ;-)

Dass diese Benutzer auch immer alles übertreiben müssen. Vielleicht hält 
ja die gute SMA6T ihr Werbeversprechen und macht rechtzeitig einen 
Kurzschluss.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Wilhelm M. schrieb:
>> Du kommst allerdings mit weniger Vorbedingungen aus, wenn Du
>> gleich deinen DT mit einem besser eingeschränkten Wertebereich nimmt.
>
> Traditionell gibt es für die Kanalnummern ein typedef enum. Das spielt
> mit einem switch schon ganz gut zusammen, nur beim Funktionsaufruf muss
> C (oder doch wieder ich?) noch üben.

In dem Fall musst Du noch üben ;-)

>>> oder return -1; dann muss eben der Aufrufer damit umgehen.
>> Da schießt Du Dir im obigen Code allerdings auch selbst ins Knie, denn
>> das wird dort nicht behandelt.
>
> Manchmal ergibt sich das ganz natürlich: -1mV ist kleiner als 21V :)

Ja, leider. Und schon wieder ist der Leser des Codes maximal an der Nase 
herum geführt.

> Da
> ist C wieder im Vorteil, eine richtige Sprache würde so eine Mischung
> aus mV und Status garnicht erlauben.

Wie ich schon so oft sagte: nutze einfach die Goodies von C++, auch wenn 
Du sonst rein prozedural in C-Style programmierst.

von mh (Gast)


Lesenswert?

Bauform B. schrieb:
> Manchmal ergibt sich das ganz natürlich: -1mV ist kleiner als 21V :) Da
> ist C wieder im Vorteil, eine richtige Sprache würde so eine Mischung
> aus mV und Status garnicht erlauben.

Welche "richtige" Sprache verbietet dir denn einen "signed integer" in 
dieser Form zu benutzen?

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Ja, leider. Und schon wieder ist der Leser des Codes maximal an der Nase
> herum geführt.

Jetzt sei nicht so negativ und freu dich mal an den kleinen Geschenken 
der Natur ;)

mh schrieb:
> Bauform B. schrieb:
>> Manchmal ergibt sich das ganz natürlich: -1mV ist kleiner als 21V :) Da
>> ist C wieder im Vorteil, eine richtige Sprache würde so eine Mischung
>> aus mV und Status garnicht erlauben.
>
> Welche "richtige" Sprache verbietet dir denn einen "signed integer" in
> dieser Form zu benutzen?

Na, mal nur geträumt: von Ada würde ich sowas erwarten. Eine Funktion 
kann entweder einen Status (eine endliche Anzahl definierter Werte) oder 
eine Spannung (bipolar oder unipolar) liefern, aber nicht beides 
abwechselnd. Wenn die Spannung den definierten Wertebereich 
überschreitet, gibt es innerhalb der Funktion einen Laufzeitfehler. 
Dank vollständiger Tests kann der im Feld ja nicht auftreten. Oder so.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:

> Na, mal nur geträumt: von Ada würde ich sowas erwarten.

Nimmst halt C++ und std::optional<>: fertig!
Die kleinen Goodies ;-)

von mh (Gast)


Lesenswert?

Bauform B. schrieb:
> Na, mal nur geträumt: von Ada würde ich sowas erwarten. Eine Funktion
> kann entweder einen Status (eine endliche Anzahl definierter Werte) oder
> eine Spannung (bipolar oder unipolar) liefern, aber nicht beides
> abwechselnd. Wenn die Spannung den definierten Wertebereich
> überschreitet, gibt es innerhalb der Funktion einen Laufzeitfehler.
> Dank vollständiger Tests kann der im Feld ja nicht auftreten. Oder so.

In Ada hält dich auch niemand davon ab einen integer mit range [-1, 
2^16] zu nehmen und den Fehlerfall in der -1 zu codieren.

von Bauform B. (bauformb)


Lesenswert?

mh schrieb:
> In Ada hält dich auch niemand davon ab einen integer mit range [-1,
> 2^16] zu nehmen und den Fehlerfall in der -1 zu codieren.

Das ist ja beruhigend, dass man sich in jeder Sprache in den Fuß 
schießen kann :)

Wilhelm M. schrieb:
> Nimmst halt C++ und std::optional<>: fertig!

Und er nun wieder, hartnäckig wie ein Versicherungsverkäufer ;)
Ich hab's probiert und konnte meine struct nicht statisch 
initialisieren, also sowas
1
info_struct info = {
2
   .name = "foo",
3
   .version = 1
4
};

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:

> Ich hab's probiert und konnte meine struct nicht statisch
> initialisieren, also sowas
1
info_struct info = {
2
>    .name = "foo",
3
>    .version = 1
4
> };

Und was soll das sein?

Was soll eine "statische" Initialisierung sein?

Ein struct braucht nicht den Typenamen xxx_struct zu bekommen.

Zeige die Definition der Klasse.

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Ein struct braucht nicht den Typenamen xxx_struct zu bekommen.

Was sonst? xxx_t ist verboten.

> Zeige die Definition der Klasse.

Klassen gibt's nicht, nur C
1
typedef struct image_struct { 
2
   uint32_t        *top_of_stack;       // 00   initial SP - Offset 
3
   union { 
4
      crt0_type       *crt0;            // 04   initial PC(system) 
5
      crt1_type       *crt1;            // 04   initial PC(programs) 
6
   }; 
7
   crt0_type       *nmi;                // 08 
8
   crt0_type       *hard_fault;         // 0c 
9
   char             info_version;       // 10   'E' 
10
   image_type_enum  image_type;         // 11   bootloader, program, shm,... 
11
   uint16_t         dev_id;             // 12   DBGMCU_IDCODE.DEV_ID 
12
   uint32_t         crc;                // 14 S 
13
   const void      *text_start;         // 18 
14
   const void      *text_end;           // 1c   last byte of text segment 
15
   char             name[7 + 1];        // 20 S 
16
   uint32_t         source_date_epoch;  // 28 S unix time 
17
   uint32_t         shm_version;        // 2c S unix time 
18
   uint32_t         syslib_version;     // 30 S unix time 
19
   uint32_t        *ram_start;          // 34 
20
   void            *bss_end;            // 38 
21
   const char * const *syslog_text;     // 3c 
22
   const void      *const_start;        // 40 
23
   uint16_t         shm_size;           // 44   Bytes 
24
   uint16_t         start_delay;        // 46   ms 
25
   int8_t           prio;               // 48   priority 1 .. 99 
26
   uint8_t          privilege;          // 49   1 = privileged 
27
   uint16_t         rsvd4a;             // 4a 
28
   uint32_t         rsvd4c;             // 4c 
29
   char             version_string[];   // 50 
30
} image_struct; 
31
32
const image_struct __attribute__ ((section ("$image_hdr"), used))
33
image_hdr = {
34
   .top_of_stack      = &tos,
35
   .crt1              = crt1,
36
37
   .privilege         = 0,
38
   .prio              = 7,
39
   .start_delay       = 0,
40
   .const_start       = NULL,
41
   .syslog_text       = &syslog_text,
42
   .ram_start         = &stack,
43
   .bss_end           = &bss_end,
44
   .shm_size          = sizeof (shm_struct),
45
46
   .info_version      = HDR_VERSION,
47
   .image_type        = IT_PROGRAM,
48
   .dev_id            = 0x464,
49
   .text_start        = &image_hdr,
50
   .text_end          = 0,
51
   .syslib_version    = (uint32_t) &SYSLIB_VERSION,
52
   .source_date_epoch = 0,
53
   .crc               = 0,
54
};

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Wilhelm M. schrieb:
>> Ein struct braucht nicht den Typenamen xxx_struct zu bekommen.
>
> Was sonst? xxx_t ist verboten.

Wer sagt das?

>> Zeige die Definition der Klasse.

Bauform B. schrieb:
> Wilhelm M. schrieb:
>> Nimmst halt C++ und std::optional<>: fertig!
>
> Und er nun wieder, hartnäckig wie ein Versicherungsverkäufer ;)
> Ich hab's probiert und konnte meine struct nicht statisch
> initialisieren, also sowasinfo_struct info = {
>    .name = "foo",
>    .version = 1
> };

Du schreibst: ich hab's probiert ... das bezieht sich auf meine Äußerung 
zu C++.

Nun scheibst Du:

Bauform B. schrieb:

> Klassen gibt's nicht, nur C

Und dann auch noch was ganz anderes.
Was willst Du mit den ganzen zusammenhanglosen Bemerkungen eigentlich 
erreichen?

> typedef struct image_struct {
>    uint32_t        *top_of_stack;       // 00   initial SP - Offset
>    union {
>       crt0_type       *crt0;            // 04   initial PC(system)
>       crt1_type       *crt1;            // 04   initial PC(programs)
>    };
>    crt0_type       *nmi;                // 08
>    crt0_type       *hard_fault;         // 0c
>    char             info_version;       // 10   'E'
>    image_type_enum  image_type;         // 11   bootloader, program,
> shm,...
>    uint16_t         dev_id;             // 12   DBGMCU_IDCODE.DEV_ID
>    uint32_t         crc;                // 14 S
>    const void      *text_start;         // 18
>    const void      *text_end;           // 1c   last byte of text
> segment
>    char             name[7 + 1];        // 20 S
>    uint32_t         source_date_epoch;  // 28 S unix time
>    uint32_t         shm_version;        // 2c S unix time
>    uint32_t         syslib_version;     // 30 S unix time
>    uint32_t        *ram_start;          // 34
>    void            *bss_end;            // 38
>    const char * const *syslog_text;     // 3c
>    const void      *const_start;        // 40
>    uint16_t         shm_size;           // 44   Bytes
>    uint16_t         start_delay;        // 46   ms
>    int8_t           prio;               // 48   priority 1 .. 99
>    uint8_t          privilege;          // 49   1 = privileged
>    uint16_t         rsvd4a;             // 4a
>    uint32_t         rsvd4c;             // 4c
>    char             version_string[];   // 50
> } image_struct;
> const image_struct _attribute_ ((section ("$image_hdr"), used))
> image_hdr = {
>    .top_of_stack      = &tos,
>    .crt1              = crt1,
>    .privilege         = 0,
>    .prio              = 7,
>    .start_delay       = 0,
>    .const_start       = NULL,
>    .syslog_text       = &syslog_text,
>    .ram_start         = &stack,
>    .bss_end           = &bss_end,
>    .shm_size          = sizeof (shm_struct),
>    .info_version      = HDR_VERSION,
>    .image_type        = IT_PROGRAM,
>    .dev_id            = 0x464,
>    .text_start        = &image_hdr,
>    .text_end          = 0,
>    .syslib_version    = (uint32_t) &SYSLIB_VERSION,
>    .source_date_epoch = 0,
>    .crc               = 0,
> };

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
>> Was sonst? xxx_t ist verboten.
> Wer sagt das?

Der C-Standard reserviert erstmal nur int*_t und uint*_t, aber auch 
Sachen wie tss_t oder wctrans_t. Das alleine würde mir schon reichen. 
POSIX reserviert sich dann gleich alles, was auf _t endet:
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html
1
B.2.12 Data Types
2
Defined Types
3
The requirement that additional types defined in this section end
4
in "_t" was prompted by the problem of name space pollution. It is
5
difficult to define a type (where that type is not one defined by
6
POSIX.1-2017) in one header file and use it in another without adding
7
symbols to the name space of the program. To allow implementors to
8
provide their own types, all conforming applications are required
9
to avoid symbols ending in "_t", which permits the implementor to
10
provide additional types. Because a major use of types is in the
11
definition of structure members, which can (and in many cases must)
12
be added to the structures defined in POSIX.1-2017, the need for
13
additional types is compelling.

> Was willst Du mit den ganzen zusammenhanglosen Bemerkungen eigentlich
> erreichen?

C Lernen; jede Bemerkung hilft. Ich finde, wir sind garnicht so weit vom 
Thema abgekommen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Wilhelm M. schrieb:
>>> Was sonst? xxx_t ist verboten.
>> Wer sagt das?
>
> Der C-Standard reserviert erstmal nur int*_t und uint*_t, aber auch
> Sachen wie tss_t oder wctrans_t. Das alleine würde mir schon reichen.
> POSIX reserviert sich dann gleich alles, was auf _t endet:
> https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html

Wieder Salamitaktik: jetzt kommt auch noch Posix-conformance dazu.
Auf einem µC-Board?

>> Was willst Du mit den ganzen zusammenhanglosen Bemerkungen eigentlich
>> erreichen?
>
> C Lernen; jede Bemerkung hilft. Ich finde, wir sind garnicht so weit vom
> Thema abgekommen.

Dann schau wenigstens einmal über den Tellerrand hinaus!

von Rolf M. (rmagnus)


Lesenswert?

Bauform B. schrieb:
> Der C-Standard reserviert erstmal nur int*_t und uint*_t, aber auch
> Sachen wie tss_t oder wctrans_t. Das alleine würde mir schon reichen.

Da gibts noch einiges mehr, unter anderem alles, was auf is, to oder str 
gefolgt von einem Kleinbuchstaben anfängt, wie z.B.

Bauform B. schrieb:
> uint32_t        *top_of_stack;       // 00   initial SP - Offset

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

Rolf M. schrieb:
> Da gibts noch einiges mehr, unter anderem alles, was auf is, to oder str
> gefolgt von einem Kleinbuchstaben anfängt, wie z.B.

Das ist gemein, das gilt nicht! Aber warte, bis ich das kleingedruckte 
gelesen hab'.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:
> Rolf M. schrieb:
>> Da gibts noch einiges mehr, unter anderem alles, was auf is, to oder str
>> gefolgt von einem Kleinbuchstaben anfängt, wie z.B.
>
> Das ist gemein, das gilt nicht! Aber warte, bis ich das kleingedruckte
> gelesen hab'.

Nimm einen C++ namespace für Deine eigenen Bezeichner ;-)

von Jan K. (jan_k)


Lesenswert?

Wilhelm M. schrieb:
> Bauform B. schrieb:
>
>> Ich hab's probiert und konnte meine struct nicht statisch
>> initialisieren, also sowas
1
info_struct info = {
2
>>    .name = "foo",
3
>>    .version = 1
4
>> };
>
> Und was soll das sein?
>
> Was soll eine "statische" Initialisierung sein?
>
> Ein struct braucht nicht den Typenamen xxx_struct zu bekommen.
>
> Zeige die Definition der Klasse.

Er meint designated initializers. C only. Sehr hilfreich. Einer der 
Gründe, weswegen wir unseren Code ebenfalls nicht einfach so mit C++ 
übersetzen können, um die "kleinen goodies" zu benutzen.

von Sonnenkönig (Gast)


Lesenswert?

Wilhelm M. schrieb:
> typedef struct image_struct { ... } image_struct;

Maximal degeneriert.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jan K. schrieb:

> Er meint designated initializers.

Habe ich schon verstanden ...

> C only.

Nicht mehr: ist auch in C++ verfügbar (weiß grad nicht ob 17 oder 20)

von Jan K. (jan_k)


Lesenswert?

Sonnenkönig schrieb:
> Wilhelm M. schrieb:
>> typedef struct image_struct { ... } image_struct;
>
> Maximal degeneriert.

Völlig Standard in C. Auch wenn ich es so schreiben würde (und auch 
tue):

typedef struct _module_image_t { ... } module_image_t;

Vorteile:
- man sieht, dass es ein Typ sein soll
- struct muss nicht mehr geschrieben werden
- intellsisense/memory watch kann die structs auflösen und anzeigen
- man keine Namenskonflikte durch den module "namespace"

von Jan K. (jan_k)


Lesenswert?

Wilhelm M. schrieb:
> Jan K. schrieb:
>
>> Er meint designated initializers.
>
> Habe ich schon verstanden ...

Sorry, alles klar :)
>
>> C only.
>
> Nicht mehr: ist auch in C++ verfügbar (weiß grad nicht ob 17 oder 20)

Guter Hinweis, danke! Gibt's leider kaum zertifizierte Compiler für, 
Keil ist zumindest bei C++14. Ist sicherlich ne andere Baustelle als 
hier gezeigt.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Ich habe keinen MISRA-Compiler zur Hand. Trotzdem würde ich das 
Original-Konstrukt in einem Code-Review rauswerfen.

Nur so aus Jux, hier mal das Original umgeschrieben. Zum Busy-Waiting, 
fehlender Durchschnittsbildung usw. wurde eigentlich alles gesagt, ich 
habe das trotzdem mal so gelassen. Und ohne das durch einen Compiler 
gesendet zu haben:
1
// Header with pseudo units - mainly for documentation purposes
2
3
// Basic time units
4
#define MS    (1)
5
#define SEC   (1000 * MS)
6
7
// Basic voltage unit
8
#define RAW_V (1)
9
10
// ///////////////////////////////////////////////////////////////////////
11
12
#define MIN_WAIT_TIME    (255 * MS)
13
#define SAMPLE_INTERVALL (16 * MS)
14
// rounded:
15
#define REQUIRED_SAMPES  (MIN_WAIT_TIME / SAMPLE_INTERVALL + 1)
16
17
#define MAX_WAIT_TIME    (30 * SEC)
18
// rounded:
19
#define MAX_SAMPLES      (MAX_WAIT_TIME / SAMPLE_INTERVALL + 1)
20
21
#define RAW_24V          (8192 * RAW_V)
22
#define MIN_RAW_VOLTAGE  (RAW_24V)
23
24
#if REQUIRED_SAMPLES < 1
25
#   error REQUIRED_SAMPLES
26
#endif
27
#if MAX_SAMPLES < REQUIRED_SAMPLES
28
#   error MAX_SAMPLES < REQUIRED_SAMPLES
29
#endif
30
31
// ///////////////////////////////////////////////////////////////////////
32
33
int needed_samples = REQUIRED_SAMPLES;
34
int remaining_total_samples = MAX_SAMPLES;
35
36
while(needed_samples > 0 && remaining_total_samples-- > 0) {
37
    needed_samples = (adc_read_24V() < MIN_RAW_VOLTAGE) ? needed_samples - 1 : REQUIRED_SAMPLES;
38
    sleep_ms(SAMPLE_INTERVALL);
39
}
40
41
if(remaining_total_samples <= 0) {
42
    // Record fail state in persistent memory, turn failure LED on, etc.
43
44
    halt(); // panic(), etc.
45
}

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Jan K. schrieb:
>
>> Er meint designated initializers.
>
> Habe ich schon verstanden ...
>
>> C only.
>
> Nicht mehr: ist auch in C++ verfügbar (weiß grad nicht ob 17 oder 20)

20, allerdings mit einer Einschränkung: Man muss die Initializer in der 
Reihenfolge angeben, in der die Member-Variablen definiert sind. Das ist 
für mich ein erheblicher Nachteil.

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Nicht mehr: ist auch in C++ verfügbar (weiß grad nicht ob 17 oder 20)

g++ kann es irgendwie schon länger, sogar mit der unschönen 
C++20-Einschränkung, daß die Reihenfolge der Elemente eingehalten werden 
muß.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wilhelm M. schrieb:
>> Nicht mehr: ist auch in C++ verfügbar (weiß grad nicht ob 17 oder 20)
>
> g++ kann es irgendwie schon länger, sogar mit der unschönen
> C++20-Einschränkung, daß die Reihenfolge der Elemente eingehalten werden
> muß.

Ja, viele Sachen kann der GCC schon viel länger, weil er oft die 
Referenzimplementierungsplattform ist:

https://en.cppreference.com/w/cpp/compiler_support

von Michael F. (Firma: IAR Systems) (michael_iar)


Lesenswert?

Hannes J. schrieb:
> Nur so aus Jux, hier mal das Original umgeschrieben. Zum Busy-Waiting,
> fehlender Durchschnittsbildung usw. wurde eigentlich alles gesagt, ich
> habe das trotzdem mal so gelassen. Und ohne das durch einen Compiler
> gesendet zu haben:

In Zeile 15 fehlt ein "L", da ansonsten die Error-Direktive immer 
zuschlägt: REQUIRED_SAMPES => REQUIRED_SAMPLES

;-)

Und MISRA-C2012 sagt folgendes zur while-loop:
1
The right-hand operand of a logical && or || operator shall not contain side effects  - MISRAC2012-Rule-13.5

Eventuell ist das ja genau so gewollt, wenn nicht, kann man aber mit 
solchen Konstrukten einiges an "Spaß" haben...

Gruß,
Michael

von Bauform B. (bauformb)


Lesenswert?

Vielen Dank für die Aufmerksamkeit -- auch wenn mir mache Vorschläge ein 
wenig abwegig erscheinen.

Michael F. schrieb:
> In Zeile 15 fehlt ein "L", da ansonsten die Error-Direktive immer
> zuschlägt: REQUIRED_SAMPES => REQUIRED_SAMPLES
>
> ;-)
Und deswegen vermeiden wir #define. Ja, es gibt auch 1 bis 2 nützliche 
Anwendungen, aber in diesem Beispiel wären sogar die magischen Zahlen 
besser gewesen :)

Jan K. schrieb:
> typedef struct _module_image_t { ... } module_image_t;

wir haben doch gerade festgestellt, dass manche Namen reserviert sind. 
Das "t" am Ende mag ja noch als Gewohnheitsrecht durchgehen, aber "_" am 
Anfang? Echt jetzt?

OK, das ist ja alles nicht tragisch, aber das?!
Rolf M. schrieb:
> Bauform B. schrieb:
>> Der C-Standard reserviert erstmal nur int*_t und uint*_t, aber auch
>> Sachen wie tss_t oder wctrans_t. Das alleine würde mir schon reichen.
>
> Da gibts noch einiges mehr, unter anderem alles, was auf is, to oder str
> gefolgt von einem Kleinbuchstaben anfängt, wie z.B.
>
> Bauform B. schrieb:
>> uint32_t        *top_of_stack;       // 00   initial SP - Offset

top_of_stack als struct member dürfte nicht betroffen sein, aber als 
globale Variable? Bei Funktionen wie torque_limiter() ist es dann ganz 
vorbei. Natürlich gibt es den Namen nicht in der libc, aber er ist 
"potentially reserved" und darf jederzeit eingeführt werden. Was auch 
noch nicht schlimm wäre, wenn es nur nicht kompilieren würde -- aber 
meine Verwendung ist ab dann undefined behavior!

Der einzige Trost: ein uC-Programm kann man i.d.R. mit -ffreestanding 
-nostdlib übersetzen (auch mit FreeRTOS?). Dann gibt es toupper(), 
strcmp() usw. nicht und Macros benutzt man ja nicht. Also bleiben 
praktisch nur die Einschränkungen der Sprache selbst. Und ich muss auf 
assert() verzichten, das gibt's nicht im freestanding environment :(

https://en.cppreference.com/w/c/language/identifier

: Bearbeitet durch User
von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Michael F. schrieb:
> Und MISRA-C2012 sagt folgendes zur while-loop:
>
1
> The right-hand operand of a logical && or || operator shall not contain 
2
> side effects  - MISRAC2012-Rule-13.5
3
>

Och wie gemein.

> Eventuell ist das ja genau so gewollt,

Das war in der Tat so gewollt. Der Schleifenabbruch wenn needed_samples 
0 ist sollte Vorrang vor einem weiteren Decrement von 
remaining_total_samples haben, damit man bei einem erfolgreichen Warten 
nicht in letzter Sekunde in das folgende if(remaining_total_samples <= 
0) läuft.

Na ja, muss man halt die Schleife etwas umschreiben. Das if() sollte man 
sowieso auf if(needed_samples > 0) ändern.

Bauform B. schrieb:
> Und deswegen vermeiden wir #define. Ja, es gibt auch 1 bis 2 nützliche
> Anwendungen, aber in diesem Beispiel wären sogar die magischen Zahlen
> besser gewesen :)

Ehrlich? Kindergarten-Privatmeinung. Man kann sich natürlich alle Arten 
von abstrusen Begründungen basteln wenn man keine #defines mag.

Ich hätte übrigens eher gedacht das sich jemand über den als Köder 
reingequetschten ?:-Operator aufregt. Na ja, so kann man sich irren.

von Rolf M. (rmagnus)


Lesenswert?

Bauform B. schrieb:
> top_of_stack als struct member dürfte nicht betroffen sein, aber als
> globale Variable?

Richtig, die Namen sind für mögliche Funktionen vorgesehen und sind 
daher nur dort problematisch, wo es es mit einer Funktion zum 
Namenskonflikt kommen kann.

> Bei Funktionen wie torque_limiter() ist es dann ganz
> vorbei. Natürlich gibt es den Namen nicht in der libc, aber er ist
> "potentially reserved" und darf jederzeit eingeführt werden. Was auch
> noch nicht schlimm wäre, wenn es nur nicht kompilieren würde -- aber
> meine Verwendung ist ab dann undefined behavior!

Der Standard behält sich vor, in bestimmten Teilen der 
Standardbibliothek neue Funktionen mit solchen Namen einzuführen. Für 
die besagten mit 'to' wären das z.B. <ctype.h> und <wctype.h>. Nun ist 
es recht unwahrscheinlich, dass je eine Funktion namenes 
torque_limiter() ihren Weg dorthin findet, aber ja, streng genommen ist 
das Verhalten dann undefiniert.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hannes J. schrieb:

>
1
> 
2
> #define MIN_WAIT_TIME    (255 * MS)
3
> #define SAMPLE_INTERVALL (16 * MS)
4
> // rounded:
5
> #define REQUIRED_SAMPES  (MIN_WAIT_TIME / SAMPLE_INTERVALL + 1)
6
...
7
> #if REQUIRED_SAMPLES < 1
8
> #   error REQUIRED_SAMPLES
9
> #endif
10
> #if MAX_SAMPLES < REQUIRED_SAMPLES
11
> #   error MAX_SAMPLES < REQUIRED_SAMPLES
12
> #endif
13
> 
14
> int needed_samples = REQUIRED_SAMPLES;
15
> int remaining_total_samples = MAX_SAMPLES;
16
> 
17
> while(needed_samples > 0 && remaining_total_samples-- > 0) {
18
>     needed_samples = (adc_read_24V() < MIN_RAW_VOLTAGE) ? needed_samples 
19
> - 1 : REQUIRED_SAMPLES;
20
>     sleep_ms(SAMPLE_INTERVALL);
21
> }
22
> 
23
> if(remaining_total_samples <= 0) {
24
>     // Record fail state in persistent memory, turn failure LED on, etc.
25
> 
26
>     halt(); // panic(), etc.
27
> }
28
>

Das ist jetzt ja noch schlimmer:

- viele unscoped defines
- wieder lokale variablen im umgebenden scope

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Das ist jetzt ja noch schlimmer:

Vor allem ist das Grundproblem nicht behoben:

Keine - gar keine - Funktion wenn bloss die Spannungsmessung nicht geht.

Dabei lauft der Prozessor, hat seinen RESET und brown out hinter sich, 
er hat seine Betriebsspannung und könnte kommunizieren und dort deutlich 
sagen "ich bin da aber die 24V liegen erst bei 23V"

Stattdessen darf der Anwender nun fluchen weil er nicht weiss was der 
Grund für die Fehlfunktion ist.

von Bauform B. (bauformb)


Lesenswert?

MaWin schrieb:
> Vor allem ist das Grundproblem nicht behoben:
>
> Keine - gar keine - Funktion wenn bloss die Spannungsmessung nicht geht.

Immerhin leuchtet ab 4 bis 5 Volt eine rote LED. Und ansonsten ist das 
so gewollt, ich sehe einfach das Problem nicht.

Wie wahrscheinlich ist denn ein Fehler der Messung selbst? Da sind außer 
dem uC selbst 2 Widerstände, ein C und eine Durchkontaktierung 
beteiligt. Ja, ich benutze die 3.3V als Referenz. Wenn also der 
3.3V-Regler 4.2V liefert wird eine zu niedrige Spannung gemessen. Wenn 
deshalb nicht eingeschaltet wird, ist das durchaus kein Nachteil. Aber 
mit 6V läuft der uC sowieso nicht mehr.

Meinetwegen benutze zusätzlich ich die interne Referenz. Allerdings wird 
dadurch die Wahrscheinlichkeit größer, dass das Teil nicht startet.

von Bauform B. (bauformb)


Lesenswert?

Wilhelm M. schrieb:
> Hannes J. schrieb:
1
#define MIN_WAIT_TIME    (255 * MS)
2
#define SAMPLE_INTERVALL (16 * MS)
3
// (...)
4
  halt(); // panic(), etc.
> Das ist jetzt ja noch schlimmer:

Jetzt sind wir uns wieder einig :)

Bauform B. schrieb:
> ein uC-Programm kann man i.d.R. mit -ffreestanding -nostdlib
> übersetzen
> Und ich muss auf assert() verzichten, das gibt's nicht im
> freestanding environment :(

Hast du evt. dazu eine Empfehlung? Das ist mir jetzt wegen der 
reservierten Namen aufgefallen. -nostdlib benutze ich schon von Anfang 
an, aber das verhindert nicht, dass beliebige libc-Header benutzt 
werden. Bei der rundum-sorglos Toolchain ist natürlich newlib und 
assert.h dabei.

Einzelne Funktionen wie logf() oder memset() hab' ich irgendwo geklaut, 
warum sollte ich assert.h nicht auch kopieren?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Bauform B. schrieb:

> Einzelne Funktionen wie logf() oder memset() hab' ich irgendwo geklaut,
> warum sollte ich assert.h nicht auch kopieren?

Das assert()-Macro musst Du Dir eh selbst basteln, denn in einem µC 
Projekt wird Dir ein terminate() wenig nutzen. Du wirst wohl mit einem 
Pin wackeln müssen oder was immer Du willst.

von MaWin (Gast)


Lesenswert?

Bauform B. schrieb:
> ich sehe einfach das Problem nicht.

Das ist ja das Problem.

Dein Ding geht erst los, wenn die 24V erfolgreich gemessen werden.

Wenn während der Benutzung die 24V ein Problem haben, interessiert das 
offenbar kein Schwein.

Wir wissen nicht, in welchem Umfeld, aber MISRA klingt nach automotive 
und 24V nach LKW, also ein Steuergerät.

Man will keine Steuergeräte die nicht starten aus obskuren Gründen. Es 
können dutzende Gründe sein und man ist bei Fehlfunktion am Rätselraten. 
Man will, so bald man das Steuergerät erreichen kann, eine anständige 
Fehlerdiagnose, ublicherweise einen per OBD abfragbetes Fehlerflag für 
jede Möglichkeit.

Wenn dein Ding angestöpselt wird,  in der Hand gehalten wird, und für 
jede Fehlermöglichkeit eine LED hat

grün Power ist da

gelb uC hat RESET geschafft

rot die 24V sind nicht da

blau alles ok

Mag man ja feedback haben, aber mir scheint es müssten viele weitere LED 
sein.

von Bauform B. (bauformb)


Lesenswert?

MaWin schrieb:
> Wenn während der Benutzung die 24V ein Problem haben, interessiert das
> offenbar kein Schwein.

Naja, Schwein hat halt andere Prioritäten. Das Programm ist durchaus 
dran interessiert.

> Wir wissen nicht, in welchem Umfeld, aber MISRA klingt nach automotive
> und 24V nach LKW, also ein Steuergerät.

MISRA ist mehr eine Art Hobby. In jeder Regel steckt ein Körnchen 
Wahrheit und jeder Tipp hilft. LKW trifft es schon fast: Diesellok, aber 
nur ein abgesetztes Display.

> Mag man ja feedback haben, aber mir scheint es müssten
> viele weitere LED sein.

Ja, die hätte ich auch gerne. Aber aufpassen: grüne LED könnten mit 
einem grünen Signal verwechselt werden.
Wie wäre denn das:
 rot: uC hat Reset nicht geschafft
 rot blinkend: 24V sind nicht da
 aus: alles gut (mit Text im Display)

von Jan K. (jan_k776)


Lesenswert?

Bauform B. schrieb:
> Jan K. schrieb:
>> typedef struct _module_image_t { ... } module_image_t;
>
> wir haben doch gerade festgestellt, dass manche Namen reserviert sind.
> Das "t" am Ende mag ja noch als Gewohnheitsrecht durchgehen, aber "_" am
> Anfang? Echt jetzt?

Jo, ist völlig standard. "_" ist nicht verboten, "__" hingegen schon. 
Machen auch richtig große Libs so, z.B. GTK 
https://github.com/GNOME/gtk/blob/main/gtk/gtkactionmuxerprivate.h, oder 
ganz viele ARM libs, siehe z.B. 
https://github.com/ARM-software/CMSIS_5/blob/develop/CMSIS/Driver/Include/Driver_CAN.h

Es mag sein, dass "_t" in POSIX verboten ist, dann nimmt man halt "_T" 
oder "_type"... POSIX ist jetzt aber auch nicht soo verbreitet bei µCs.

Was stört dich daran? Sonst müsste man sich doch noch einen Namen für 
die struct ausdenken. Ich weiß, dass es unnamed structs gibt, aber wie 
erwähnt hilft es immens, wenn der debugger die struct auch auflösen kann 
und ich sehe, wie diese heißt.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Jan K. schrieb:
> Jo, ist völlig standard. "_" ist nicht verboten,

Doch, wenn darauf ein Großbuchstabe folgt, schon:

"All identifiers that begin with an underscore and either an uppercase 
letter or another underscore are always reserved for any use."

 und außerdem:
"All identifiers that begin with an underscore are always reserved for 
use as identifiers with file scope in both the ordinary and tag name 
spaces."

von Experte (Gast)


Lesenswert?

Na, wurde jetzt schon die MISRA-Konforme Warteschleife gefunden?

Eine klitzekleines Randproblemchen sorgt für Stunden sinnloser 
Beschäftigung, und am Ende kommt was völlig verkrüppeltes heraus, nur 
damit es Regeln zur vermeintlichen Qualitätssteigerung entspricht.

Herrlich, wie diese Idiotie für ausufernde Kosten sorgt...

von MaWin (Gast)


Lesenswert?

Bauform B. schrieb:
> Diesellok, aber nur ein abgesetztes Display.

Display heisst, das Ding kann selber anzeigen, was ihm nicht behagt.

Lass es hochfahren, sich initialisieren, und frage dann in einer 
Schleife ständig alles wissenswerte ab. Und wenn eben die 24V ausser 
Toleranz sind, kann sie das anzeigen, aber trotzdem auch die anderen 
Messwerte.

von Christian F. (cfrey)


Lesenswert?

Bauform B. schrieb:
> aber MISRA erlaubt das nicht.

MISRA erlaubt das durchaus. Es empfiehlt sich, die gesamten 
MISRA-Guidelines durchzulesen, und nicht nur das Kapitel mit den 
eigentlichen Regeln.

Bei der beanstandeten Regel geht es (wahrscheinlich) um die "Rule 14.2: 
A for loop shall be well-formed". Diese ist in der Required-Kategorie, 
d.h. um MISRA-konform zu sein muss man das Nichteinhalten der Regel 
dokumentieren (entweder für das gesamte Projekt oder für die konkrete 
Stelle im Code).

von Bauform B. (bauformb)


Lesenswert?

Jan K. schrieb:
> Jo, ist völlig standard. "_" ist nicht verboten, "__" hingegen schon.
> Machen auch richtig große Libs so, z.B. GTK
> oder ganz viele ARM libs

Die beiden, und die CMSIS libs besonders, meinen wahrscheinlich, dass 
sie zur "implementation" gehören wie die libc. Wenn wir das mal fein 
unterteilen:
 1) gcc
 2) libgcc
 3) newlib
 4) CMSIS
 5) meine eigene lib
 6) mein Programm
wo ist dann die Grenze? 1 und 2 dürfen natürlich alles und 6 natürlich 
nichts davon. Wenn ich unbedingt den _ am Anfang benutzen wollte, könnte 
man höchstens über 5 verhandeln.

> Es mag sein, dass "_t" in POSIX verboten ist, dann nimmt man halt "_T"
> oder "_type"...

oder eben _struct und _enum; kaum länger als _type und ein klein wenig 
mehr Information. Was soll xxx_struct schon anderes sein als ein type 
(da, wo es benutzt wird)?

> POSIX ist jetzt aber auch nicht soo verbreitet bei µCs.

Naja, eine ausgewachsene shell eher nicht, write() und sbrk() gibt's 
auch in der newlib-nano. Und es gibt ein paar bewährte Regeln. Warum 
soll man das auf einem uC nicht nutzen?

> Ich weiß, dass es unnamed structs gibt

Die braucht man im inneren einer union, aber sonst will die doch keiner 
haben.

von Bauform B. (bauformb)


Lesenswert?

MaWin schrieb:
> Display heisst, das Ding kann selber anzeigen, was ihm nicht behagt.

Jein, nicht mit weniger als 7 Volt. Die magischen 8192mV aus meiner 
ursprünglichen Frage waren direkt aus dem Programm. Dann dachte ich, es 
wird verständlicher, wenn ich hier 21V schreibe. War wohl nichts ;)

Also noch weiter weg von Thema ;) Die Idee dabei ist, kurze Ausfälle der 
24V abzufangen. Dabei sinkt die Spannung nicht bis auf 0 und manche 
Chips laufen normal weiter und andere (besonders das Display) nicht. 
Deshalb erzeuge ich gleich einen Reset, sobald die Spannung unter 7V 
sinkt. Nach einem Reset wird kaum mehr initialisiert als der ADC, dann 
kommt die berüchtigte Warteschleife und erst danach die restliche 
Initialisierung.

Ich finde, das passt gut zu solchen Start-Verweigerungen wie bei einem 
Flash-CRC-Fehler oder nach dem 3. Watchdog-Reset oder so.

von Bauform B. (bauformb)


Lesenswert?

Christian F. schrieb:
> Bei der beanstandeten Regel geht es (wahrscheinlich) um die "Rule 14.2:
> A for loop shall be well-formed". Diese ist in der Required-Kategorie

Ja und ja, das ist schon klar. MISRA ist hier kein Muss, mir geht es nur 
um besser lesbare Programme. Da ist jeder Hinweis willkommen, wie z.B. 
die 14.2. Nur fand ich die Begründung dazu etwas dünn.

In dem Moment, wenn ich etwas schreibe, sieht das für mich natürlich 
total eindeutig und übersichtlich aus. Und sobald es funktioniert, ist 
es auch schon wieder vergessen. C bietet so viele Möglichkeiten, etwas 
unleserlich zu schreiben. Welche Sprache hat schon einen Obfuscated Code 
Contest?

von MaWin (Gast)


Lesenswert?

Bauform B. schrieb:
> (besonders das Display) nicht. Deshalb erzeuge ich gleich einen Reset,
> sobald die Spannung unter 7V sinkt

Na ja, man könnte auch das Display neu initialisieren, wenn man meint 
das hätte es nötig (reagiert nicht, jede Minute, Spannung war mal 
niedriger wobei man dabei kurze Einbruche mitbekommen muss) und nicht 
gleich mit Allem von vor beginnen. Der uC sollte durch brown out 
detection selbst neu starten wenn er es nötig hat.

Ich initialisiere Peripherie auch öfter neu als nötig, einfach damit es 
nicht vergessen wird.

Mich nervt z.B. Arduino 4 bit LCD 16x2 das nach Reset aber ohne power 
loss nicht korrekt re-initialisiert wird, weil es schon auf 4 bit 
initialisiert war, und die Leute das nicht behandeln sondern glauben es 
wäre 8 bit.

von Experte (Gast)


Lesenswert?

Bauform B. schrieb:
> MISRA ist hier kein Muss, mir geht es nur
> um besser lesbare Programme.

Dann empfehle ich Hirn statt MISRA.

von Rolf M. (rmagnus)


Lesenswert?

Experte schrieb:
> Dann empfehle ich Hirn statt MISRA.

Amen dazu. MISRA kann Hirn nicht ersetzen, auch wenn viele das meinen.

von Bauform B. (bauformb)


Lesenswert?

Amen hin oder her, im Nachbar-Thread gab es einen guten Tipp zum Thema:

Beitrag "Re: Warnung bei if (variable) bla();"

von Experte (Gast)


Lesenswert?

Rolf M. schrieb:
> MISRA kann Hirn nicht ersetzen, auch wenn viele das meinen.

MISRA ist wie Diätpillen. Nutzlos für die Verwender, aber eine Goldgrube 
für die Verkäufer.

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.