Forum: Mikrocontroller und Digitale Elektronik Viele logische Verknüpfungen.


von Gerhard (Gast)


Lesenswert?

if (baud==300 || baud==600 || baud==1200 || baud==2400 || baud==4800 ||
    baud==9600 || baud==14400 || baud==19200 || baud==28800 || 
baud==38400 || baud==57600 || baud==115200 || baud==230400)
    {
    }

kann man das auch anders schreiben? Irgendwie so ähnlich wie in 
switch/case, da gibts ja auch eine Liste von Argumenten:


case 300: case 600: etc

Am erzeugten Code wird sich ja eher nichts ändern - mir gefällt das so 
einfach nicht besonders :-)

von Stefan K. (stefan64)


Lesenswert?

switch (baud)
{
  case 300:
  case 600:
  case 1200:
  case 2400:
  case 4800:
  case 9600:
  case 14400:
  case 19200:
  case 28800:
  case 38400:
  case 57600:
  case 115200:
  case 230400:
   // entspricht if() Zweig
   break;

  default:
  // entspricht else Zweig
    break;
}

Gruß, Stefan

von Rene H. (Gast)


Lesenswert?

1
switch(baud) {
2
    case 300:
3
    case 600:
4
    case 1200:
5
       ...
6
}

wäre eine Möglichkeit.

Grüsse,
René

von Dr. Sommer (Gast)


Lesenswert?

Schöner und einfacher zu erweitern ist ja die Suche in einem Array:
1
#include <iostream>
2
#include <cstdint>
3
#include <algorithm>
4
#include <array>
5
6
7
// Variante 1
8
bool isbaud1 (uint32_t baud) {
9
  static const uint32_t baudrates [] = 
10
    { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400 };
11
  
12
  return std::any_of (baudrates, baudrates + (sizeof(baudrates) / sizeof (baudrates [0])),
13
    [&](uint32_t x) { return x == baud; });
14
}
15
16
// Variante 2
17
bool isbaud2 (uint32_t baud) {
18
  static const std::array<uint32_t,13> baudrates
19
    {{ 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400 }};
20
  
21
  return std::find (baudrates.begin (), baudrates.end (), baud) != baudrates.end ();
22
}
23
24
// Nur zum Testen:
25
int main () {
26
  uint32_t testbaud [] = { 300, 42, 14400, 230400 };
27
  
28
  std::cout << "Variante 1: ";
29
  for (auto b : testbaud)
30
    std::cout << b << " => " << std::boolalpha << isbaud1 (b) << ", ";
31
  std::cout << std::endl;
32
  
33
  std::cout << "Variante 2: ";
34
  for (auto b : testbaud)
35
    std::cout << b << " => " << std::boolalpha << isbaud2 (b) << ", ";
36
  std::cout << std::endl;
37
  
38
  return 0;
39
}

Somit lässt sich die Liste leichter verändern, und man kann einen 
Schleifenalgorithmus zum Suchen nutzen.

von Rene H. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Schöner und einfacher zu erweitern ist ja die Suche in einem Array:

Wir sind hier in dem Unterforum Mikrocontroller. Auf einem 
Mikrocontroller will man keine stdlib einsetzen.

Grüsse,
René

von Dr. Sommer (Gast)


Lesenswert?

Rene H. schrieb:
> Wir sind hier in dem Unterforum Mikrocontroller. Auf einem
> Mikrocontroller will man keine stdlib einsetzen.
Warum will man das nicht? Ich mache das jeden Tag.

von Rene H. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Rene H. schrieb:
>> Wir sind hier in dem Unterforum Mikrocontroller. Auf einem
>> Mikrocontroller will man keine stdlib einsetzen.
> Warum will man das nicht? Ich mache das jeden Tag.

Ok, das erstaunt mich jetzt. Ich kenne die stdlib so, dass sie ziemlich 
viel unnötigen Speicher verbraucht.
Aber ich werde das gerne mal auf einem Mikrocontroller testen.

Grüsse,
René

von Dr. Sommer (Gast)


Angehängte Dateien:

Lesenswert?

Rene H. schrieb:
> Ok, das erstaunt mich jetzt. Ich kenne die stdlib so, dass sie ziemlich
> viel unnötigen Speicher verbraucht.
Das ist Quatsch.

Rene H. schrieb:
> Aber ich werde das gerne mal auf einem Mikrocontroller testen.
Gerne.

Im Anhang das Kompilat obigen Codes für Cortex-M4. Wie zu sehen ist, hat 
sich der Compiler hier dazu entschieden die Schleife zur Erhöhung der 
Performance auszurollen. Aber es kein überhaupt keinen "unnötigen 
Speicherverbrauch".

von Stefan K. (stefan64)


Lesenswert?

Ich finde den Vorschlag von  Dr. Sommer eine interessante Lösung. Für 
ein paar Baudraten vielleicht etwas oversized. Aber spätestens wenn die 
Liste der Vergleiche größer wird, wird das sehr interessant. Und das .s 
Beispiel zeigt eindrucksvoll, wie flexibel der Compiler da optimieren 
kann.

Gruß, Stefan

P.S.:
Die negativen Bewertungen für  Dr. Sommers Beiträge kann ich mir nur mit 
ideologischen Scheuklappen erklären.

von Rene H. (Gast)


Lesenswert?

Stefan K. schrieb:
> Die negativen Bewertungen für  Dr. Sommers Beiträge kann ich mir nur mit
> ideologischen Scheuklappen erklären.

Na, dem kann man doch entgegenwirken :-) +1 von mir auf alle Fälle.

von Amateur (Gast)


Lesenswert?

Ist
                       switch (baud) {
if (baud==300 ||         case 300:
    baud==600 ||         case 600:
    baud==1200 ||        case 1200:
    baud==2400 ||        case 2400:
    baud==4800 ||        case 4800:
    baud==9600 ||        case 9600:
    baud==14400 ||       case 14400:
    baud==19200 ||       case 19200:
    baud==28800 ||       case 28800:
    baud==38400 ||       case 38400:
    baud==57600 ||       case 57600:
    baud==115200 ||      case 115200:
    baud==230400)        case 230400:
    {                         break;
    }
    else                 default:
    {
    }                   }

da wirklich ein Unterschied?

Natürlich war die Ausgangsformulierung eher mittelprächtig, kompakt, 
aber kaum lesbar.

von Rene H. (Gast)


Lesenswert?

Amateur schrieb:
> da wirklich ein Unterschied?

Vom Resultat (also Kompilat) nicht, nein. Nur besser lesbar.

Grüsse,
René

von Joachim B. (jar)


Lesenswert?

Rene H. schrieb:
> +1 von mir auf alle Fälle

dito

von Marc (Gast)


Lesenswert?

Ein + an Dr. Sommer!
Ich habe mit C++ auf embedded systemen nur positive Erfahrung (wenn man 
weiss was man tut... aber das ist bei C auch so...)

Zum Besänftigen aller C Puristen noch eine Variante, die in C und C++ 
läuft:
1
bool isbaud(uint32_t baud)
2
{
3
   static const uint32_t baudrates[] =
4
   { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400, 0}; //Beachte Arrayabschluss mit 0
5
   size_t i = 0;
6
   while (baudrates[i])
7
   {
8
      if (baudrates[i] == baud) return true;
9
      i++;
10
   }
11
12
   return false;
13
}

von Dr. Sommer (Gast)


Lesenswert?

Marc schrieb:
> Zum Besänftigen aller C Puristen noch eine Variante, die in C und C++
> läuft:
Na hoffentlich muss man das Schema nie auf etwas übertragen, wo eine 0 
im Array stehen kann! Da ist eine schlichte for-Schleife doch einfacher:
1
#include <stdbool.h>
2
#include <stdint.h>
3
#include <stddef.h>
4
5
bool isbaud (uint32_t baud) {
6
  static const uint32_t baudrates[] =
7
    { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400};
8
  static const size_t length = sizeof (baudrates) / sizeof (baudrates [0]);
9
  for (size_t i = 0; i < length; ++i)
10
    if (baudrates [i] == baud)
11
      return true;
12
  return false;
13
}

Solche Konstrukte nach Schema F können Compiler typischerweise auch 
besser optimieren...

von Falk B. (falk)


Lesenswert?

@ Dr. Sommer (Gast)

>  for (size_t i = 0; i < length; ++i)
>    if (baudrates [i] == baud)
>      return true;
>  return false;

Ein "schönes" Beispiel, wie man in C NICHT programmieren sollte! Bei 
Schleifen, if(), switch() etc. IMMER Klammern setzen! Dann gibt es auch 
keine bösen Überraschungen und die Struktur ist besser lesbar sowie 
besser erweiterbar!

von Dr. Sommer (Gast)


Lesenswert?

Falk B. schrieb:
> Ein "schönes" Beispiel, wie man in C NICHT programmieren sollte! Bei
> Schleifen, if(), switch() etc. IMMER Klammern setzen!
Das ist jetzt sehr dogmatisch. Wenn man einen vernünftigen Editor 
verwendet und immer schön einrückt, ist das halb so schlimm, dafür 
kompakter. Aber um Klammern ging es eigentlich gar nicht...

von Nase (Gast)


Lesenswert?

Falk B. schrieb:
> Ein "schönes" Beispiel, wie man in C NICHT programmieren sollte!

Dann hättest du aber eher anmerken sollen, dass man UINT32_C benutzen 
soll. Vorallem auf dem Mikrocontroller...

von W.S. (Gast)


Lesenswert?

Gerhard schrieb:
> kann man das auch anders schreiben?

Ja, natürlich kann man sowas ganz anders schreiben und auch MACHEN. Also 
Kopf mal aus der Furche erheben und ne wirklich bessere Lösung 
praktizieren.

Normalerweise hat man ja seinen zuständigen Systemtakt und man hat einen 
Baudratengenerator im UART-Core enthalten, den man einstellen muß.

Anstatt sich eine Latte von Standard-Baudraten mit if(xyz)... zu 
programmieren und dann die Einstellwerte aus ner Tabelle oder so zu 
entnehmen, kann man die Registerwerte für den Baudratengenerator auch 
(Überraschung!...) errechnen. Das kostet in den meisten Fällen nicht 
mehr an Code als eine Latte von if..else und ist deutlich flexibler.

Ich mache das immer so bei den UART-Standardcores, die in den LPCxxxx 
verbaut sind - und da die Dinger nen fraktionalen BRG enthalten, kommt 
man selbst bei exotischen Baudraten damit hin.

Also nicht "wie kann ich eine stupide Methode besser schreiben", sondern 
"wie kann ich eine bessere Methode schreiben".

W.S.

von Joachim B. (jar)


Lesenswert?

W.S. schrieb:
> Normalerweise hat man ja seinen zuständigen Systemtakt und man hat einen
> Baudratengenerator im UART-Core enthalten, den man einstellen muß.
>
> Anstatt sich eine Latte von Standard-Baudraten mit if(xyz)... zu
> programmieren und dann die Einstellwerte aus ner Tabelle oder so zu
> entnehmen, kann man die Registerwerte für den Baudratengenerator auch
> (Überraschung!...) errechnen. Das kostet in den meisten Fällen nicht
> mehr an Code als eine Latte von if..else und ist deutlich flexibler.

wo steht denn das der TO den Baudratengenerator einstellen will?

Kann man zwar erraten, aber (falsche) Annahmen sind oft der erste 
Fehler.

Vielleicht hat er was ganz anderes im Sinn und da ist Rechnen wirklich 
nicht angesagt.

von Gerhard (Gast)


Lesenswert?

Vielen Dank schon mal für die rege Beteiligung. Da  werde ich mich 
gleich mal durchforsten.

@W.S. Setzen, 6.
Du hast da Sachen reininterpretiert, die gar zur Diskussion standen.
Konkret gehts darum, die UART1 vom Anwender zur Laufzeit einzustellen. 
Und das soll eben nur gemacht werden können, wenn korrekte und 
vorgesehene Parameter geschickt werden.
"9600,8N1" wird akzeptiert, eingestellt und bestätigt. 19300 eben nicht.

Ausserdem hatte ich so ein Problem schon mal in einem anderen 
Zusammenhang.

von Peter D. (peda)


Lesenswert?

Wenn man Code sparen will, kann man das auch berechnen:
1
bool isbaud (uint32_t baud){
2
  for( ; !(baud & 1); baud >>= 1 ){
3
    switch( baud ){
4
      case 300:
5
      case 14400: return 1;
6
      case 76800:
7
      case 460800: return 0;
8
    }
9
  }
10
  return 0;
11
}

von Marc (Gast)


Lesenswert?

> Na hoffentlich muss man das Schema nie auf etwas übertragen, wo eine 0
> im Array stehen kann! Da ist eine schlichte for-Schleife doch
> einfacher

Die Variante mit for.... habe ich natürlich auch, aber ich dachte das 
ist jetzt zu langweilig. Die 0 Terminierung hat auch Vorteile, z.B. muss 
die Arraygröße nicht bekannt sein. Sehr interessant, wenn zur Laufzeit 
unterschiedliche Listen genutzt werden...

Jetzt musst du mir erklären weshalb diese Variante "einfacher" sein soll 
als eine for Schleife. Wird eine for Schleife tatsächlich besser 
optimiert als die Wert-Terminierung?


Falk B. schrieb: > Ein "schönes" Beispiel, wie man in C NICHT 
programmieren sollte! Bei > Schleifen, if(), switch() etc. IMMER 
Klammern setzen!

Dafür hab ich AStyle benutzt um die Einrückung korrekt zu machen, Ätsch. 
Spass beiseite: du hast recht, eigentlich sollte man sich angewöhnen 
immer Klammern zu nutzen. Aber es ist korrekter C Code...

von Marc (Gast)


Lesenswert?

Nase schrieb:
>
> Dann hättest du aber eher anmerken sollen, dass man UINT32_C benutzen
> soll. Vorallem auf dem Mikrocontroller...

Ich verstehe den Kontext nicht. Kannst Du das näher erklären?

von Dr. Sommer (Gast)


Lesenswert?

Marc schrieb:
> Sehr interessant, wenn zur Laufzeit
> unterschiedliche Listen genutzt werden...
Da würde ich doch lieber die Länge seperat mit übergeben, genauso wie 
std::vector in C++ es im Endeffekt auch macht. Da gibts dann keine 
Kopfschmerzen wenn man doch mal eine 0 braucht. Dinge mit 0 zu 
terminieren ist so eine C-Unsitte...

Marc schrieb:
> Jetzt musst du mir erklären weshalb diese Variante "einfacher" sein soll
> als eine for Schleife.
Hauptsächlich weil man auf einen Blick sieht, wie oft die Schleife 
durchläuft, wie der Schleifenzähler sich ändert und so. Das 0815-Schema 
hilft einfach dabei, die Schleife sofort zu verstehen. Bei der 
while-Schleife muss man erst den Schleifenkörper und die 0 suchen um das 
zu erfassen.

Marc schrieb:
> Wird eine for Schleife tatsächlich besser
> optimiert als die Wert-Terminierung?
Kann unter Umständen passieren, wenn der Compiler z.B. die Zählrichtung 
umkehrt weil auf manchen Plattformen der Vergleich mit 0 schneller ist. 
Ist aber stark vom Einzelfall abhängig.

von Marc (Gast)


Lesenswert?

Du hast mich noch nicht überzeugt... Ich kenne einige Codeteile, die 
solche Terminierungen nutzen. Nicht zuletzt arbeiten C Strings auch 
so.Wenn jemand die sizeof(array) / sizeof(array[0] Variante falsch nutzt 
geht es auch schief...

Im Beispiel sind Array und Zugriffsmethode lokal beieinander, man 
erkennt den Bezug, zumal ich extra einen Kommentar eingefügt habe.

von Walter S. (avatar)


Lesenswert?

Dr. Sommer schrieb:
> Kann unter Umständen passieren, wenn der Compiler z.B. die Zählrichtung
> umkehrt weil auf manchen Plattformen der Vergleich mit 0 schneller ist.
> Ist aber stark vom Einzelfall abhängig.

und der Compiler kann den Code so weit analysieren dass er die 
Zählrichtung umdrehen darf? in em Beispiel mag es gehen, in den meisten 
anderen Schleifen aber nicht.
Den Code deswegen "optimiert" zu schreiben ist in 99,9% der Fälle völlig 
unnötig. Und in den restlichen 0,1% macht man sich lieber Gedanken um 
den prinzipiellen Algorithmus

von Dr. Sommer (Gast)


Lesenswert?

Marc schrieb:
> Ich kenne einige Codeteile, die solche Terminierungen nutzen.
Ja, da gibt es jede Menge von.

Marc schrieb:
> Nicht zuletzt arbeiten C Strings auch so.
Ja, und das ist der Auslöser für diverse Bugs, die manchmal auch 
sicherheitskritisch sind (ich meine da gab es z.B. mal was in PHP)...

Marc schrieb:
> Wenn jemand die sizeof(array) / sizeof(array[0] Variante falsch nutzt
> geht es auch schief...
Deswegen kapselt man das mit entsprechenden Funktionen/Makros/struct's. 
In C++ geht das dann besser, wie in std::vector vorgemacht.

Marc schrieb:
> Im Beispiel sind Array und Zugriffsmethode lokal beieinander, man
> erkennt den Bezug, zumal ich extra einen Kommentar eingefügt habe.
Ja da geht's. Aber ich finde so etwas einfach hässlich, weil es nicht 
universell ist und nur ein "Workaround", um die Länge nicht immer 
explizit mit angeben zu müssen, weil das in C zu anstrengend ist.

von Walter S. (avatar)


Lesenswert?

Dr. Sommer schrieb:
bool isbaud2 (uint32_t baud) {
   static const std::array<uint32_t,13> baudrates
     {{ 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400,
 57600, 115200, 230400 }};

   return std::find (baudrates.begin (), baudrates.end (), baud) !=
 baudrates.end ();
}

Amateur schrieb:
 if (baud==300 ||
     baud==600 ||
     baud==1200 ||
     baud==2400 ||
     baud==4800 ||
     baud==9600 ||
     baud==14400 ||
     baud==19200 ||
     baud==28800 ||
     baud==38400 ||
     baud==57600 ||
     baud==115200 ||
     baud==230400)
     {
     }
     else
     {
     }

ich sehe in der zweiten Varianten auf einen Blick was gemacht wird
und könnte sofort eine Änderung vornehmen,
in der ersten Variante muss ich erst 3x hinschauen was da gemacht wird

von Marc (Gast)


Lesenswert?

Dr. Sommer schrieb:
...

Gute Argumentation.
Ich hätte diese Lösung auch nicht in jeder Applikation genutzt.


Verstehst du was Nase meint?

>Nase schrieb:

>> Dann hättest du aber eher anmerken sollen, dass man UINT32_C benutzen
    soll. Vorallem auf dem Mikrocontroller...

von Dr. Sommer (Gast)


Lesenswert?

Marc schrieb:
> Verstehst du was Nase meint?
Ich vermute er meint man solle uint32_t -Literale der Form 
UINT32_C(230400) schreiben, damit der Compiler garantiert den richtigen 
Typ erwischt, denn bei Literalen wird niemals von alleine etwas genommen 
das kleiner "int" ist. Das ist in diesem Fall aber unnötig, da durch den 
Array-Typ der Integer-Typ bereits vorgegeben ist. In C++ würde ich aber, 
falls es denn nötig ist, lieber uint32_t { 230400 } verwenden, denn das 
funktioniert garantiert mit allen Integer-Typen und aliasen, ohne dass 
man noch zu jedem Integer-Typ ein Makro bräuchte.

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.