Forum: Mikrocontroller und Digitale Elektronik Per UART Zeichen bis CR/LF einlesen und String auswerten


von Alex (Gast)


Lesenswert?

Guten Abend Leute,

Ich möchte per UART gerne einen String einlesen bis dieser mit CR/LF 
beendet wird.
Abhängig davon welcher String eingelesen wurde, möchte ich verschiedene 
Aktionen durchführen.

Zum Hintergrund: später soll der AVR Befehe über UART entgegen senden 
und dann über das RFM12-Modul verschiedene Funk-Kommandos senden.

Im Folgenden ein Demo-Programm, das ich zum Nachvollziehen meines 
Problems geschrieben habe.
Ich verwende die UART-Library von Peter Fleury.

Ob das richtige Commando geschickt wurde, prüfe ich testweise über eine 
LED und einen Vergleich via strcmp().
Das funktioniert nicht und selbst die Prüfung auf "t" als erstes Zeichen 
in meinem Char-Array schlägt fehl.
Auch wenn ich versuche mit über uart_puts() den gesendeten String 
zurücksenden  zu lassen, kriege ich keine Ausgabe.

Hier mein Code:
1
#define F_CPU 8000000UL
2
3
#include <stdlib.h>
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include <avr/pgmspace.h>
7
#include <util/delay.h>
8
#include <string.h>
9
#include "uart.h"
10
11
#define UART_BAUD_RATE      9600      
12
13
#define ON(p,v)   ( p |=  (1<<v) );
14
#define OFF(p,v)   ( p &= ~(1<<v) );
15
16
int main(void)
17
{
18
  char c, command[21];
19
  int ci = 0;
20
21
  // Init
22
  ON(DDRD, PD5);                    // Debug-LED an PD5
23
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
24
  sei();
25
    
26
  // Kontrolle (per UART & LED leuchten)
27
  ON(PORTD, PD5);
28
  _delay_ms(1000);
29
  OFF(PORTD, PD5);
30
  uart_puts("Begin:");
31
   
32
    for(;;)
33
    {
34
    // Per UART Zeichen einlesen
35
    c = uart_getc();
36
      if (!( c & UART_NO_DATA ))
37
    {
38
      // In Char-Array "command" speichern & testweise ausgeben
39
      command[ci] = c;
40
      ci = ci<20 ? ci+1 : 0;
41
      uart_putc(c);
42
      
43
      // Wenn CR empfangen wurde
44
      if (c == 0x0d) 
45
      {
46
        // String mit 0-Byte terminieren
47
        command[ci+1] = '\0';
48
        ci = 0;
49
50
        // Prüfen ob eingegeben Text "test" ist (testweise mit "t" probiert
51
        //if (strcmp(command, "test") == 0) {
52
        if (command[0] == 't') {
53
          ON(PORTD, PD5);      // LED an
54
        }
55
        else {
56
          OFF(PORTD, PD5);    // sonst LED aus
57
        }
58
        uart_puts("\r\n");
59
        uart_puts(command);      // Hier zeigt er auch nichts an
60
      }
61
    }
62
    }
63
    
64
}

Vielleicht hat ja einer von euch eine Idee, was ich falsch mache.

VG Alex

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

wenn nicht mal die rücksenden der Zeichen geht, dann geht die 
Kommunikation vermutlich nicht.

http://www.mikrocontroller.net/articles/AVR_Checkliste#UART.2FUSART

von holger (Gast)


Lesenswert?

c = uart_getc();

Welchen Datentyp gibt uart_getc() zurück?

von Alex (Gast)


Lesenswert?

Die Kommunikation funktioniert, ich kann also ausschließen, dass es ein 
generelles Problem, wie in der Checkliste beschrieben, ist.

Holger: uart_getc gibt mir ein unsigned int zurück. Nachdem ich jetzt 
meine Varibale c als unsigned int geändert habe, geht es.
Hatte das in den ganzen Stunden wohl schon zu oft umgeändert.

Ich hab schon alles mögliche durchprobiert, aber das war der Fehler! Ein 
"t" als ersten Buchstaben kann ich jetzt also erkennen!

Noch etwas: wenn ich jetzt meinen Vergleich mit Strcmp mache, matched 
der nicht. Ich habe beim strcmp() jetzt schon "test", "test\n" und 
"test\r\n" probiert, leider ohne Erfolg. Hast Du da noch einen Tip?

Vielen Dank schon mal,

LG Alex

von Der Pflug (Gast)


Lesenswert?

Sowas macht man mit einer Zustandsmaschine. Das ist viel einfacher zu 
debuggen. such hier oder bei gurgel nach Zustandsmaschine resp state 
machine

von Karl H. (kbuchegg)


Lesenswert?

Alex schrieb:

> Noch etwas: wenn ich jetzt meinen Vergleich mit Strcmp mache, matched
> der nicht. Ich habe beim strcmp() jetzt schon "test", "test\n" und
> "test\r\n" probiert, leider ohne Erfolg. Hast Du da noch einen Tip?

Du hast eine UART. Du kannst dir über die UART Zeichen (und auch 
Strings) ausgeben lassen.

Sieh dir an, was der AVR gekriegt hat! Lass es dir ausgeben.

Vor dem Kommandostring und dahinter gibst du ein Sonderzeichen aus, zb 
ein '#', damit du auch die Position der im String enthaltenen 
Zeilenvorschübe in der Ausgabe sehen kannst. Gibst du am Terminal "test" 
ein, dann muss die Ausgabe
1
#test#
lauten.

Sie darf weder
1
#
2
test#
sein, noch darf sie
1
#test
2
#
sein, und auch
1
#
2
test
3
#
ist aus dem Rennen. Aus der Position deines Sonderzeichens in Relation 
zum Text kannst du erkennen, wo im String ein Zeilenvorschub ist.

Und lass die Sache mit dem Ringbuffer sein. Wenn dein Commandostring zu 
lang ist, dann machst du keinen Wraparound sondern ignorierst weitere 
Zeichen (und gibst gegebenenfalls eine Fehler,eldung aus). Jedes 
Kommando fängt im Array immer ganz vorne an. Dinge wie \r oder \n willst 
du erst gar nicht im String haben, die speicherst du daher nicht. Ein \n 
löst dann zwar die Weiterverarbeitung des empfangenen Strings aus, du 
willst aber den \n nicht im String speichern. Der behindert dich nur bei 
der Bearbeitung des Strings, liefert dir aber ansonsten keine 
Information.

: Bearbeitet durch User
von Frank (Gast)


Lesenswert?

Ich habe sowas ähnliches mit einem Arduino und Ethernet gemacht. Dabei 
lese ich in jedem Loop ein Zeichen aus dem Ethernet-Puffer in eine 
Fifo-Struktur (Array) fester Länge (5 Byte bzw. Char) - d.h. alle 
Array-Felder rücken eins vor, das letzte wird mit dem neuen Wert 
gefüllt, das erste fällt weg. Ebenfalls in jedem Loop prüfe ich, ob ein 
festgelegtes Anfangs- und Endezeichen auf dem ersten und letzten Platz 
dieses Arrays steht. Wenn ja, dann

- Prüfsumme checken (4. Byte)
- Kommando auslesen (2. Byte)
- Paramter auslesen (3. Byte)
- Fifo-Array leeren
- Bestätigungs-Byte zurücksenden
- Kommando ausführen
- zurück zum Loop

Die UART hat ja auch einen eigenen Puffer. Wenn man den nicht 
"zuschwemmt" sollte sich die Methode übertragen lassen ...

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.