mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik char[x]-Wert in Switch Anweisung nicht möglich?


Autor: M.B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich programmiere einen ATmega in der AVR GCC Umgebung.

Ich empfange über UART Zeichen und Strings, die ich auswerten möchte:
z.B. 'Run', Stop', 'An', 'Aus'

Dies Sind in meiner Variablen gespeichert und wollen nun ausgewertet 
werden. Meine Idee war mit switch - aber leider bekomme ich das nicht 
hin. Hier ein Ausschnitt von meinem Code:

char Data[10];
[...]

  switch (Data)
  {
    case '1':
             USART_Tx_s("Es wurde die Taste 1 gedrueckt");
             _Enter;
             break;
    case 'a':
             USART_Tx_s("Das war ein 'a'");
             _Enter;
             break;
    case 'hallo':
             USART_Tx_s("Herzlich Willkommen");
             _Enter;
             break;
    default:
             USART_Tx_s("Leider nicht im Programm.\n\r Versuchen Sie es noch einmal...");
             _Enter;
             break;
  }

Die Daten werden über UART empfangen. Ich sende sie als Echo zurück. Das 
funktioniert alles. Aber wie kann ich sie nun auswerten?

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
switch akzeptiert als Parameter int oder char, kein Array von chars. 
Möglich wäre z.B.
switch (Data[0])

Bzw. Du kannst dann nicht auf 'hallo' prüfen, sondern nur auf 'h'.

Autor: Jürgen S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Data ist ja nur die Adresse auf das Array...

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Äh, ja. Ich vergaß.
Was man manchen könnte, wenn man es mit switch machen will: Auf das 
erste Zeichen prüfen. Im case-Block dann die weiteren Zeichen checken, 
z.B. mit strcmp()?

Autor: M.B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M..., so was blödes. Gibts eine andere Möglichkeit?
Trotzdem vielen Dank für die schnellen antworten.

Autor: Christian H. (netzwanze) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nimm Dir eine verkettete Liste, in der Du an jeder Listenposition einen 
Zeiger auf einen String sowie einen Zeiger auf eine Abarbeitungsroutine 
speicherst. Diese Strings sind Deine Befehlsworte.
Wenn Du einen Befehl bekommen hast, musst Du diese Liste durchsuchen und 
bei einem Treffer die Abarbeitungsroutine anspringen.

Oder sowas (in etwa):
if      (strcmp("1", Data)) {...}
else if (strcmp("hallo", Data)) {...}
else {...}

Data muss hier natürlich Nullterminiert sein.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M.B. schrieb:
> M..., so was blödes. Gibts eine andere Möglichkeit?

es gibt 2 Möglichkeiten:
Entweder du beschränkst dich in deiner Übertragung und damit in den 
Kommandos, die du übertragen kannst, auf 1-buchstabige (dann kannst du 
switch nehmen) oder du überträgst ganze Strings, dann ist eine if - 
elseif Kette mit lauter strcmp oder strncmp angebracht.
  ...

  if( !strncmp( Data, "Run", 3 ) )
     // Bearbeite Run-Kommando

  else if( !strncmp( Data, "Stop", 4 ) )
     // Bearbeite Stop-Kommando

  else if( !strncmp( Data, "An", 2 ) )
     // Bearbeite An-Kommando

  else if( !strncmp( Data, "Aus", 3 ) )
     // Bearbeite Aus Kommando

  else
    // Bearbeite ungültiges Kommando

(und immer schön auf den Unterschied 'einzelner char' und 'String' 
achten. Strings werden mit " geschrieben!)

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, was sind die Trennzeichen zwischen den zu empfangenden Wörtern? Man 
könnte den Empfangsstring mit strtok() aufdröseln und dann in einer 
Schleife mit strcmp() weitermachen.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian H. schrieb:
> Nimm Dir eine verkettete Liste, in der Du an jeder Listenposition einen
> Zeiger auf einen String sowie einen Zeiger auf eine Abarbeitungsroutine
> speicherst. Diese Strings sind Deine Befehlsworte.
> Wenn Du einen Befehl bekommen hast, musst Du diese Liste durchsuchen und
> bei einem Treffer die Abarbeitungsroutine anspringen.

Ein Array tut es auch ;)

Autor: Christian H. (netzwanze) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon K. schrieb:
> Christian H. schrieb:
>> Nimm Dir eine verkettete Liste, in der Du an jeder Listenposition einen
>> Zeiger auf einen String sowie einen Zeiger auf eine Abarbeitungsroutine
>> speicherst. Diese Strings sind Deine Befehlsworte.
>> Wenn Du einen Befehl bekommen hast, musst Du diese Liste durchsuchen und
>> bei einem Treffer die Abarbeitungsroutine anspringen.
>
> Ein Array tut es auch ;)

Ja, meinte ich eigentlich auch. Wieso ich "verkettete Liste" geschrieben 
habe, weiß ich auch nicht. Irgenwie war ich mit den Gedanken woanders 
8-|

Also vergesst "verkettete Liste" und denkt euch an der Stelle "Array".

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian H. schrieb:
>
> if      (strcmp("1", Data)) {...}
> else if (strcmp("hallo", Data)) {...}
> else {...}
> 

Schreib lieber noch '==0' dahinter oder '!' davor :->

Autor: Christian H. (netzwanze) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt, der strcmp vergleicht ja auch auf "kleiner" bzw "größer". Nur 
bei "gleich" ist das Ergebnis 0.

Autor: M.B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, vielen Dank für eure vielen Tipps und Kommentare.
Ich werde dann mein Programm so aufbauen, das ich mit "Einzelbuchstaben" 
auskomme.
Ist zwar schade, aber einfacher. Trotzdem vielen Dank für die 
hilfreichen Tipps, die nicht nur mir helfen werden.
Da bin ich mir sicher.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M.B. schrieb:
> Ich werde dann mein Programm so aufbauen, das ich mit "Einzelbuchstaben"
> auskomme.
> Ist zwar schade, aber einfacher.

:-)
Im Long-Run ist es auch nicht einfacher.
Warte ab, bis du das erste mal einem Kommando einen Parameter mitgibst. 
Dann kommst du um Stringbearbeitung nicht mehr sinnvoll rum.

Das bischen switch versus strcmp Unterschied macht das Kraut auch nicht 
fett. Zumal es, wie Christian H. schon angedeutet hat, eine sehr schöne 
und praktikable Lösung gibt, eine zur Compilezeit gut konfigurierbare 
Kommandostruktur über ein Array von Strukturen und Funktionspointer zu 
schaffen. Und dann spielt der Unterschied Einzelbuchstabe versus 
Kommandostring praktisch so gut wie keine Rolle mehr (ausser höchstens 
durch den Speicherverbauch)


Fazit:
Es bringt nichts, sich vor Stringverarbeitung zu drücken. Irgendwann 
holt sie dich trotzdem ein :-)

Autor: vlad (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kannst das ganze ja auch succsessive überprüfen und die switch 
cases-cascadieren
wenn du zb die befehle an, aus, hoch runter hast:
char* text = inputStr;
switch(text[0])
{
  case 'a': 
    switch(text[1]){
      case 'n':
        doAnStuff();
        break;
      case 'u':
        doAusStuff();
        break;
      default:
        error();
    }  
    break;
  case 'h':
    doHochStuff();
    break;
  case 'r':
    doRunterStuff();
    break;
  default:
    error();
}

{/c]


bei wenigen befehlen mag das gehen.
Bei längeren Befehlslisten, würd ich das verallgemeinern:
alle befehle in ein Array aus struct mit text und funktionspointer packen (ascibetisch sortiert).

[c]
  typedef struct UartCommand{
     const char* cmd;
     uint8_t     Len;  // um nicht jedes mal nach der länge fragen zu müssen
     UCCallback  actionRoutine;
  }UartCommand;

#define COMMAND_COUNT 4
static const UartCommand myCommands[COMMAND_COUNT] = 
{ 
  {.cmd = "an",       .len=2, .actionRoutine = &doAnStuff()},
  {.cmd = "arbeiten", .len=8, .actionRoutine = &doArbeitenStuff()},
  {.cmd = "aus",      .len=3, .actionRoutine = &doAusStuff()},
  {.cmd = "runter",   .len=6, .actionRoutine = &doRunterStuff()},
}

parseInput( const char* input)
{
  uint8_t anfang = 0;
  uint8_t ende   = COMMAND_COUNT;
  do{
    // zeichenweise durch die Liste gehen und schauen, welche infrage kommen
    // anfangs- und endeposition merken, bei 'a' also 0 2
    // immer auf länge der zechenketten achten!
    // nun wieder von vorn anfangen und liste von 0-2 durchsuche nach 2. Zeichen schauen
  while( anfang!=ende )

  myCommands[anfang].actionRoutine( input+myCommands[anfang].len );

}

Is natürlich nicht komplett, aber das Prinzip sollte klar sein.

Damit könnte man sogar eine autocompletion bauen, Dh der Benutzer tippt 
ein Zeichen und der µC bietet mögliche Kommandos an.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.