Forum: Compiler & IDEs if (c >= 'A') zulässig?


von Terra (Gast)


Lesenswert?

Hallo zusammen,

ich bin gerade dabei Daten welche vom Uart kommen zu sortieren, 
Großbuchstaben möchte ich abfangen.
1
.
2
.
3
/*
4
* Get received character from ringbuffer
5
* uart_getc() returns in the lower byte the received character and 
6
* in the higher byte (bitmask) the last receive error
7
* UART_NO_DATA is returned when no data is available.
8
*/
9
unsigned int ch = uart_getc();
10
11
if ((ch&0x00FF) == 'A') ruleTheWorld();
12
//oder
13
if ((ch<<1) == 'A') ruleTheWorld();
14
//oder
15
if ((char)(ch) == 'A') ruleTheWorld();
16
// oder
17
if ((char)(ch) == 65) ruleTheWorld();

All diese Beispiele fangen meine Buchstaben A nicht ab.

Meine Frage an euch ist also: Wie prüft man Character in C korrekt ab?

Lg,
Marc

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Terra schrieb:
> All diese Beispiele fangen meine Buchstaben A nicht ab.

Was heißt für Dich "abfangen"? Aufrufen von "ruleTheWorld"?

Alle Beispiele bis auf eines sind gleichwertig, und alle sollten 
funktionieren.

1
if ((ch<<1) == 'A') ruleTheWorld();

Das ist Quark. Gequirlt, gerührt und geschüttelt.

Probier doch mal das hier:
1
unsigned int ch = 'A';
2
3
if ((ch&0x00FF) == 'A') ruleTheWorld();
4
//oder
5
if ((char)(ch) == 'A') ruleTheWorld();
6
// oder
7
if ((char)(ch) == 65) ruleTheWorld();

von mse2 (Gast)


Lesenswert?

Terra schrieb:
> All diese Beispiele fangen meine Buchstaben A nicht ab.
Müssten sie aber. Dann würde ich mal vermuten, dass in Deinem ch aus 
irgendwelchen Gründen einfach kein 'A' drinnen ist.

von Helmut L. (helmi1)


Lesenswert?

Terra schrieb:
> Meine Frage an euch ist also: Wie prüft man Character in C korrekt ab?

Da gibt es die Funktionen:

isalpha(x)   true wenn es ein Buchstabe ist
islower(x)   true wenn es ein Kleinbuchstabe ist
isupper(x)   true wenn es ein Grossbuchstabe ist
isalnum(x)   true wenn es Buchstabe oder Ziffer ist
isdigit(x)   true wenn es eine Ziffer ist
isxdigit(x)  true wenn es eine Hex Ziffer ist

alles in ctype.h

von Ralf G. (ralg)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Probier doch mal das hier:
> unsigned int ch = 'A';

Steht das 'A' dann nicht im High-Byte?

von Fabian O. (xfr)


Lesenswert?

Ralf G. schrieb:
> Rufus Τ. Firefly schrieb:
>> Probier doch mal das hier:
>> unsigned int ch = 'A';
>
> Steht das 'A' dann nicht im High-Byte?

Nein. Das ist nichts anderes als:
1
unsigned int ch = 65;

von Ralf G. (ralg)


Lesenswert?

Ralf G. schrieb:
> Steht das 'A' dann nicht im High-Byte?

Richtig. Totaler Käse. Hab' überlegt, ob die zurückgegebenen 16 Bit 
vielleicht vertauscht sind, weil der Sender die 'andersrum' im Speicher 
ablegt.

von Terra (Gast)


Lesenswert?

Vielen Dank für euer Feedback.

Offenbar gibt es noch irgendwo einen anderen Fehler weil, wie gesagt 
ruleTheWorld() nicht aufgerufen wird.

Danke für folgenden Tipp:
isupper(x)   true wenn es ein Grossbuchstabe ist

das werde ich gleich probieren. Leider habe ich kein Debuginterface, 
sonst wäre das Problem schon gefunden...

Wichtig für mich war euer Feedback, dass mein Code grundsätzlich OK ist 
und der Hinweis auf isupper().

DANKE

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ralf G. schrieb:
> Hab' überlegt, ob die zurückgegebenen 16 Bit
> vielleicht vertauscht sind, weil der Sender die 'andersrum' im Speicher
> ablegt.

Liefert denn uart_getc einen 16-Bit-Wert? Das wäre für eine UART sehr 
ungewöhnlich.

von Fabian O. (xfr)


Lesenswert?

> Liefert denn uart_getc einen 16-Bit-Wert? Das wäre für eine UART sehr
> ungewöhnlich.

Steht zumindest im Kommentar:
1
/*
2
* Get received character from ringbuffer
3
* uart_getc() returns in the lower byte the received character and 
4
* in the higher byte (bitmask) the last receive error
5
* UART_NO_DATA is returned when no data is available.
6
*/

UART_NO_DATA ist wahrscheinlich -1, um kompatibel zu EOF aus stdio.h zu 
sein. Irgendwie muss man ja kodieren, ob ein neues Zeichen empfangen 
wurde oder nicht, wenn uart_getc() nicht blockieren soll.

von J.-u. G. (juwe)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Liefert denn uart_getc einen 16-Bit-Wert? Das wäre für eine UART sehr
> ungewöhnlich.

Ja, bei der Fleury-Lib ist das so. Das obere Byte enthält den 
Fehlercode, z.B. wenn kein Zeichen gelesen wurde (UART_NO_DATA). Dieser 
Fehlercode sollte nach Aufruf von uart_getc geprüft werden. Das hat der 
TO wohl versäumt.

von Fabian O. (xfr)


Lesenswert?

Fabian O. schrieb:
> UART_NO_DATA ist wahrscheinlich -1, um kompatibel zu EOF aus stdio.h zu
> sein.

Oder auch nicht ...
1
#define UART_NO_DATA 0x0100 /* no receive data available */

Jedenfalls wäre es dann sinnvoll, das obere Byte nicht auszumaskieren. 
Denn im Fehlerfall oder bei keinen Daten kann ja alles mögliche im 
unteren Byte stehen:
1
if (ch == 'A') ruleTheWorld();

von Ralf G. (ralg)


Lesenswert?

Ralf G. schrieb:
> Steht das 'A' dann nicht im High-Byte?

War total auf dem Holzweg :-( Die 16 Bit kommen ja nicht über UART 
rein...

von Terra (Gast)


Lesenswert?

Ja, uart_getc liefert einen 16-bit Wert, genau wie ihr erkannt habt 
verwende ich die Fleury-Lib. (Darum habe ich den Kommentar mit Hinweis 
auf 16-bit mitkopiert.)

Die Fehlerbehandlung funktioniert, auch das "Echo" der Zeichen 
funktioniert, nur das Abfangen von einem String z.B. "VERSION" will 
nicht funktionieren.
1
if(ch == 'A'){
2
    uart_putcs("hello"); // Mein Aufruf
3
}else{
4
    uart_putc(ch); // Echo
5
}

Wie gesagt das "hello" wird nicht geschickt, das Echo funktioniert.

Ich werde mich noch weiter mit dem Problem beschäftigen.

DANKE

von J.-u. G. (juwe)


Lesenswert?

Terra schrieb:
> uart_putcs("hello"); // Mein Aufruf

> Wie gesagt das "hello" wird nicht geschickt,

Kein Wunder, die Stringsendefunktion heißt uart_puts(...).

von Fabian O. (xfr)


Lesenswert?

Wenn Du das Echo so schreibst, musst Du allerdings vorher das High-Byte 
geprüft haben, ob überhaupt etwas empfangen wurde! Sonst sendest Du 
ständig etwas.

Die Funktion uart_putcs() gibt es übrigens nicht in der Fleury-Lib ... 
Am besten postest Du mal den richtigen Code, dann kann man Dir bestimmt 
schnell sagen, wo der Fehler liegt.

von Terra (Gast)


Lesenswert?

Fabian, hast recht, ich werde am besten den gesamten Code posten. (Das 
isupper() geht schon :-)

Aufruf:
1
uartRX = uart_getc();
2
processUartData((char)(uartRX&0x00FF));
Funktion:
1
void processUartData(char c){
2
  
3
  static char string[6];
4
  static uint8_t i=0;
5
  
6
  if(isupper(c)){  // process only upper case characters
7
      
8
    if (i<5 && c!='\r'){
9
      string[i]=c;
10
      i++;
11
    }else{
12
      if (strcmp(&string, "VER") == 0)
13
      {
14
        uart_puts("Version 1\n");
15
      }
16
      i = 0;
17
    }
18
    
19
  }else{
20
    switch (c)
21
    {
22
      case 'a': uart_puts("Hello World\n"); break;
23
      default: uart_putc( (unsigned char)uartRX );
24
    }
25
  }

Inzwischen geht das Erkennen der Großbuchstaben, aber bei der Eingabe 
"VER" möchte ich "Version 1" ausgeben.
Das schaffe ich irgendwie nicht.

DANKE,
Marc

von Helmut L. (helmi1)


Lesenswert?

Terra schrieb:
> if (strcmp(&string, "VER") == 0)
>       {
>         uart_puts("Version 1\n");
>       }

strcmp  gibt 0 zurueck wenn gleich

du brauchst auch nicht &string zu schreiben sondern nur string.

von J.-u. G. (juwe)


Lesenswert?

Terra schrieb:
> if (strcmp(&string, "VER") == 0)

strcmp erwartet 2 C-Strings. Dein char-Array "string" ist aber kein 
C-String (Nullterminierung fehlt).

von Sebastian W. (sebastian_w29)


Lesenswert?

isupper('\r') ist nicht wahr.

von Helmut L. (helmi1)


Lesenswert?

Sebastian W. schrieb:
> isupper('\r') ist nicht wahr.

Dann sag mit doch mal bitte den Grossbuchstaben von Carrige Return.
Das darf ja auch nicht wahr sein, ist ja kein Buchstabe.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Genau!
Und damit hier:
1
    if (i<5 && c!='\r'){

die zweite Bedingung unnötig! :-)

von Fabian O. (xfr)


Lesenswert?

Außerdem fehlt der Check, ob überhaupt Daten empfangen worden sind:
1
uartRX = uart_getc();
2
if ((uartRX & 0xFF00) == 0) {
3
  processUartData(uartRX);
4
}

Und uartRX hat an der Stelle nichts zu suchen:
1
default: uart_putc( (unsigned char)uartRX );
Da gehört hin:
1
default: uart_putc(c);

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Ich würde mir ausserdem noch zwei Funktionen zum Zerlegen der 
Bestandteile aus "uartRX" basteln:
1
int uart_geterror(int);

und
1
char uart_getchar(int);

Das erspart das mühselige Lesen der Bitmasken...

von Terra (Gast)


Lesenswert?

Danke für euer Feedback.
Die Angeführten Fehler habe ich gleich ausgebessert und habe jetzt 
folgende Implementation:
1
void processUartData(char c){
2
  
3
  static char string[6];
4
  static uint8_t i=0;
5
  
6
  if(isupper(c)){  // process only upper case characters
7
      
8
    if (i<5){
9
      string[i]=c;
10
      i++;
11
      uart_puts("if works\n");
12
    }else{
13
      if (strcmp(string, "VER") == 0)
14
      {
15
        uart_puts("Version 1\n");
16
      }
17
      uart_puts("else works\n");
18
      i = 0;
19
      memset(string, 0, sizeof(string));
20
    }
21
    
22
  }else{
23
    switch (c)
24
    {
25
      case 'a': uart_puts("Hello World\n"); break;
26
      default: uart_putc(c);
27
    }
28
  }  
29
}  
30
int main(void)
31
{  
32
  io_init();
33
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
34
  sei();
35
  while(1)
36
  {
37
    /*
38
         * Get received character from ringbuffer
39
         * uart_getc() returns in the lower byte the received character and 
40
         * in the higher byte (bitmask) the last receive error
41
         * UART_NO_DATA is returned when no data is available.
42
         *
43
         */
44
        uartRX = uart_getc();
45
        if ( uartRX & UART_NO_DATA )
46
        {
47
            /* 
48
             * no data available from UART 
49
             */
50
        }
51
        else
52
        {
53
            /*
54
             * new data available from UART
55
             * check for Frame or Overrun error
56
             */
57
            if ( uartRX & UART_FRAME_ERROR )
58
            {
59
                /* Framing Error detected, i.e no stop bit detected */
60
                uart_puts_P("UART Frame Error. No stop bit?");
61
            }
62
            if ( uartRX & UART_OVERRUN_ERROR )
63
            {
64
                /* 
65
                 * Overrun, a character already present in the UART UDR register was 
66
                 * not read by the interrupt handler before the next character arrived,
67
                 * one or more received characters have been dropped
68
                 */
69
                uart_puts_P("UART Overrun Error.");
70
            }
71
            if ( uartRX & UART_BUFFER_OVERFLOW )
72
            {
73
                /* 
74
                 * We are not reading the receive buffer fast enough,
75
                 * one or more received character have been dropped 
76
                 */
77
                uart_puts_P("Buffer overflow error.");
78
            }
79
      processUartData((char)(uartRX&0x00FF));           
80
        }
81
  }     
82
}

Wenn ich "VER" an den Controller sende bekomme ich als Feedback:
if works
if works
if works

Wenn ich ein zweites Mal "VER" schicke bekomme ich:

if works
if works
else works

Cool, so weit war ich noch nie. Leider noch nicht am Ziel.

Was meint ihr?

Lg,
Marc

von J.-u. G. (juwe)


Lesenswert?

Terra schrieb:
> Wenn ich "VER" an den Controller sende bekomme ich als Feedback:
> if works
> if works
> if works
>
> Wenn ich ein zweites Mal "VER" schicke bekomme ich:
>
> if works
> if works
> else works

Ja klar. So hast Du es programmiert. Fünf mal "if works" bekommst Du, 
weil Deine Abfrage

Terra schrieb:
> if (i<5)

genau 5 mal wahr ist. Beim Empfang des zweiten "R" wird diese Bedingung 
unwahr und der else-Zweig wird ausgeführt. Es wird also eine Vergleich 
zwischen Deinem char-Array "string" mit dem Inhalt "VERVE" und dem 
char-Array "VER\0" ausgeführt. Diese beiden Arrays sind ungleich (strcmp 
liefert nicht 0). Folglich kommt die Ausgabe "else works".

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

strcmp benötigt die null-Terminierung vor dem Aufruf!

von Stefan E. (sternst)


Lesenswert?

Patrick Dohmen schrieb:
> strcmp benötigt die null-Terminierung vor dem Aufruf!

Die ist vorhanden, denn als statische Variable wird 'string' komplett 
mit 0 initialisiert (was natürlich nicht heißt, dass eine explizite 
Null-Terminierung im Code nicht sinnvoll wäre).

von Terra (Gast)


Lesenswert?

Danke, jetzt geht es endgültig!!!
Ich bin glücklich :-)
1
  static char string[4];
2
  static uint8_t i=0;
3
  
4
  if(isupper(c)){  // process only upper case characters
5
      
6
    if (i<4){
7
      string[i]=c;
8
      string[i+1]='\0';
9
      i++;
10
      
11
      if (strcmp(string, "VER") == 0)
12
      {
13
        uart_puts("Version 1\n");
14
      }
15
      
16
    }else{
17
      i = 0;
18
      memset(string, 0, sizeof(string));
19
    }
20
    
21
  }else{
22
}

Ihr hattet alle recht.

Schönen Abend noch,
Marc

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Ich dachte dabei auch eher an die Zeile:
1
memset(string, 0, sizeof(string));

...

von Karl H. (kbuchegg)


Lesenswert?

Meine Meinung:

Die Dinge trennen

* eine Zeile empfangen, wobei Zeile definiert ist als eine Abfolge von 
Zeichen, bis zum abschliessenden \r (oder \n)
* ist die Zeile vollständig, dann wird sie ausgewertet, indem sie zb mit 
einer Liste von SChlüsselwörtern verglichen wird.

Zum einen erhält man so bessere in sich abgeschlossene Einheiten. Zum 
anderen fehlt ja in deinem Programm auch noch eine Möglichkeit, wie zb 
ein Benutzer eine Fehleingabe korrigieren kann, in dem er zb mit 
Backspace das zuletzt eingegebene Zeichen wieder zurücknimmt. Oder 
machst du keine Tippfehler?


Und noch ein kleiner Hinweis.
Es ist nett, wenn du dir "if works" etc. ausgeben lässt. Aber wenn ich 
Probleme mit einem Vergleich habe, dann würde ich mir einfach mal den 
STring mit dem ich vergleiche ausgeben lassen :-)

von Karl H. (kbuchegg)


Lesenswert?

Terra schrieb:
> Danke, jetzt geht es endgültig!!!

Aber nur zufällig.

>   static char string[4];

>     if (i<4){

d.h. i kann den Wert 3 annehmen

>       string[i]=c;
>       string[i+1]='\0';

Damit greifst du hier auf string[4] zu. string[4] existiert aber nicht.

von Terra (Gast)


Lesenswert?

Karl Heinz, guter Punkt den falschen String auszugeben, macht Sinn :-)
Die Größe des Arrays habe ich auch noch um eines erhöht. Alle Befehle 
sollen aus exakt 3 Zeichen bestehen. Das funktioniert schon ganz gut.

DANKE

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.