Forum: Mikrocontroller und Digitale Elektronik Zeichenkette über UART empfangen


von Westallgäuer E. (Gast)


Lesenswert?

Hallo zusammen,

ich benütze einen Texteditor, die Avr Gcc Toolchain mit Makefile auf 
einem Macbook, und mein Mikrocontroller ist ein Atmega 328 P, und ich 
programmiere in C. Die Uart Bibliothek ist die von P. Fleury.

Es soll vom Terminalprogramm Coolterm das Wort „Start“ über Uart 
geschickt werden, und der uC drauf reagieren (momentan durch das 
Anzünden einer Led).

Was ich schon geschafft hab ist mit strcmp einen vorher initialisierten 
String mit meiner Vorgabe zu vergleichen und das Ergebnis zu verwerten, 
und auch mit strtok einen String am : zu trennen, und mit den 
Einzelstrings weiter zu machen.

Ich häng aber irgendwie beim Empfang von mehr als einem Zeichen. Mein 
Chararray schaut z. Zt. so aus char test[6] und es passen demnach 5 
Zeichen rein plus die \0, als Markierung für Stringende.

Es wird auch nach jedem Zeichen eine Variable i hochgezählt und das neue 
Zeichen „eins weiter“ in test[i] abgelegt. Funktioniert und paßt auch.

Was allerdings nicht geht ist daß wenn ich jetzt ein kürzeres Wort 
schicke, z.B. Maus, hat ja ein Zeichen weniger und es wird trotz 
if-Anweisung (c == „\n“) {i=0; test[i] = „\0“;} das Füllen vom Array 
nicht unterbrochen und der String zurückgeschickt. Es wartet immer auf 
genau 5 Zeichen, wenn ich denn noch eins schick kommt der String wieder 
zurückgeschickt ans Coolterm.

Wo ist mein Denkfehler, müßte es nicht nach dem Empfang von \n 
abbrechen?

Code zeigen kann ich wenn gewünscht erst später bin am Handy...

Vielen Dank und schöne Ostern.

von Roland F. (rhf)


Lesenswert?

Hallo,
Westallgäuer E. schrieb:
> Code zeigen kann ich wenn gewünscht erst später bin am Handy...

Das solltest du tun, vorher kann man nicht viel dazu sagen.

rhf

von STK500-Besitzer (Gast)


Lesenswert?

Anstatt hie rProsa zu liefern, wäre es angebracht, deinen Quellcode zu 
posten.

von mittelostniederfranke (Gast)


Lesenswert?

Westallgäuer E. schrieb:
> Wo ist mein Denkfehler

Der ist zunächst mal da wo du denkst dass du uns in blumigen
Worten die Funktion deines Programmes ausführlich erklären
könntest.

SCNR

von Westallgäuer E. (Gast)


Lesenswert?

Hallo,

so, jetzt hab ich wieder Zugriff, also es schaut so aus, hier im Forum 
gefunden:
1
 unsigned int c;
2
  char test[20];
3
  int i;
4
    uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
5
    sei();
6
  i=0;
7
8
    for(;;)
9
    {  
10
        c = uart_getc();
11
        if ( c & UART_NO_DATA )
12
        {
13
        }
14
15
        else
16
        {
17
            if ( c & UART_FRAME_ERROR )
18
            {
19
                uart_puts_P("UART Frame Error: ");
20
            }
21
22
            if ( c & UART_OVERRUN_ERROR )
23
            {
24
                uart_puts_P("UART Overrun Error: ");
25
            }
26
27
            if ( c & UART_BUFFER_OVERFLOW )
28
            {
29
                uart_puts_P("Buffer overflow error: ");
30
            }
31
32
if (c == '\n' || i == (20-1))
33
    {
34
      uart_puts(test);
35
      i = 0;          // zurücksetzen für nächsten String
36
      test[i] = '\0'; // leerer String
37
    }
38
  }
39
}

Ja so schauts momentan aus...wär nett wenn jemand mal drüber gucken 
könnte vielleicht ists auch bloß ne Kleinigkeit...

von STK500-Besitzer (Gast)


Lesenswert?

Westallgäuer E. schrieb:
1
> if (c == '\n' || i == (20-1))
1
if (c == '\n' )
reicht, um die Aktion am Ende eines Strings durch zu führen.

Wie wäre es, einfach mal Kommentare einzufügen, wie du das Programm 
verstehst?!

von mittelostniederfranke (Gast)


Lesenswert?

Westallgäuer E. schrieb:
> Es wird auch nach jedem Zeichen eine Variable i hochgezählt und das neue
> Zeichen „eins weiter“ in test[i] abgelegt. Funktioniert und paßt auch.

Nö.

In deinem Programm-Fragment ist nicht zu erkennen dass du das
Array <test> mit irgendetwas befüllst ausser am Ende mit der Null.

Zeige dein komplettes Programm, nicht irgendwelche Fragmente.
Die Fleury Lib brauchst nicht zeigen.

von Westallgäuer E. (Gast)


Lesenswert?

Hallo,

oh ja stimmt da hab ich was ausgelassen, mein Fehler sorry. Editieren 
vom alten Beitrag geht nicht mehr, also hier ist der Rest:
1
unsigned int c;
2
char test[20];
3
int i;
4
5
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
6
7
sei();
8
i=0;
9
for(;;)
10
    {  
11
        c = uart_getc();
12
        if ( c & UART_NO_DATA )
13
        {
14
        }
15
        else
16
        {
17
            if ( c & UART_FRAME_ERROR )
18
            {
19
                uart_puts_P("UART Frame Error: ");
20
            }
21
            if ( c & UART_OVERRUN_ERROR )
22
            {
23
                uart_puts_P("UART Overrun Error: ");
24
            }
25
26
            if ( c & UART_BUFFER_OVERFLOW )
27
            {
28
                uart_puts_P("Buffer overflow error: ");
29
            }
30
31
 test[i] = c;
32
 i++;
33
34
if (c == '\n' || i == (20-1))
35
36
    {
37
      uart_puts(test);
38
      i = 0;          // zurücksetzen für nächsten String
39
      test[i] = '\0'; // leerer String
40
    }
41
  }
42
}

Das ist alles was aktuell vorhanden ist.

von Jemand (Gast)


Lesenswert?

Kann es sein, das Coolterm ganz einfach kein "\n" sendet? Ich kenne das 
Programm leider nicht, allerdings habe ich schon andere 
Terminal-Anwendungen gesehen, bei denen das Enter-Zeichen einstellbar 
war.

von K.B. (Gast)


Lesenswert?

Ist das eine Projektarbeit? Die Aufgabenstellung kommt mir bekannt 
vor...

von Westallgäuer E. (Gast)


Lesenswert?

Jemand schrieb:
> Kann es sein, das Coolterm ganz einfach kein "\n" sendet? Ich kenne das
> Programm leider nicht, allerdings habe ich schon andere
> Terminal-Anwendungen gesehen, bei denen das Enter-Zeichen einstellbar
> war.

Gutes Argument, ich hab in den Optionen die Wahl zwischen CR, LF, und 
CR+LF. Momentan stehts auf LF, wird automatisch angehängt.

Nein Projektarbeit ists keine, ist ne reine Freizeitnummer. Aber ich hab 
mich hier im Forum am Programmcode bedient, hier: 
Beitrag "Fleury's Uart, wie string empfangen?"

Vielleicht kommts deswegen so bekannt vor...

Gruß

von Stefan F. (Gast)


Lesenswert?

> test[i] = c;
> i++;

Hier hängst du das neue Zeichen an die Zeichenkette an. Bis dahin ist 
sie aber noch nicht terminiert.

> uart_puts(test);

Hier gibst du die nicht terminierte Zeichenkette aus. Weis der Geier was 
dann passiert, jedenfalls sicher nichts gutes.

> i = 0;
> test[i] = '\0';

Hier setzt du den Index zurück und machst die Zeichenkette leer. Das 
sieht soweit OK aus.

Vor dem Ausgeben mit uart_puts() musst du ein '\0' an die Zeichenkette 
anhängen, damit uart_puts() erkennen kann, wo sie endet.

von W.S. (Gast)


Lesenswert?

Westallgäuer E. schrieb:
> Ich häng aber irgendwie beim Empfang von mehr als einem Zeichen.

Nein, du hängst noch viel früher. Was du brauchst, ist das Nachdenken 
über eine Kommandozeile, deren Aufbau und deren Auswertung.

Also nicht so einen Kuddelmuddel wegen diverser Übertragungsfehler 
machen, sondern etwa so vorgehen (ganz grob skizziert):
1. Habe eine Kommandozeile in Form eines Arrays, z.B. char Zeile[64]; 
und einen Index für diese Zeile, z.B. int idx;
2. entleere die Zeile, indem du in Zeile[0] eine 0 hineinschreibst und 
auch idx = 0; setzt.
3. Erwarte Zeichen von der Seriellen und stopfe sie mittels idx in die 
Kommandozeile, bis entweder CR auftaucht oder die Zeile voll ist. Hinter 
jedes hineiengestopfte Zeichen kommt wieder als Abschluß eine Null. An 
dieser stelle kannst du auch eine einfache Bearbeitung einbauen, wie 
z.B. Zeichen bei BS löschen
4. Ist die Zeile voll oder CR aufgetaucht, dann kommt die Auswertung. 
Setze dazu idx wieder auf 0 und inkrementiere ihn, bis du zum ersten 
nichtweißen Zeichen kommst. Dann kannst du die nachfolgenden Zeichen 
auswerten.
5. Hast du eine gültige Sequenz gefunden, dann führe die zugehörige 
Aktion aus und gehe dann wieder zu Punkt 2.

W.S.

von Westallgäuer E. (Gast)


Lesenswert?

Grüßgott,

so, habs jetzt anders geschrieben, jetzt funktionierts, alles was ich 
schicke kommt auch genau so zurück ans Terminal. Bis hierhin sag ich mal 
vielen Dank für die Hilfe. Ausschauen tuts zur Zeit so, ist 
wahrscheinlich noch nicht der Weisheit letzter Schluß, aber hey 
wenigstens machts mal was ;-)
1
uint16_t incoming = 0;
2
char input[6];
3
char input_str[6];
4
5
uint8_t i = 0;
6
        if (incoming != '\n' && incoming != '\r')
7
      {
8
9
      input[i] = incoming;
10
      i++;
11
      input[i] = '\0';   //Nullbyte dazu
12
        strcpy (input_str, input);  // strcpy (Ziel, Quelle)
13
        i = 0;          // zurücksetzen für nächsten String
14
        input[i] = '\0'; // leerer String
15
      
16
      }

Jetzt hab ich aber noch ne andere Frage...und zwar will ich jetzt ja mit 
den empfangenen Daten was machen, also habe ich folgenden Code 
geschrieben:
1
if (strcmp (input_str, "Test") == 0)
2
  {
3
                PORTB &= ~(1 << PB1); //Led geht an zum Test
4
                
5
                // hier sollen denn noch andere Sachen passieren
6
  }

Aus einem mir bisher unbekannten Grund wird der Code in den {} oben nie 
ausgeführt.

Habe ich stattdessen statt input_str ein global deklariertes Chararray, 
funktioniert es:
1
char str[6] = "Test";
2
3
if (strcmp (str, "Test") == 0)
4
  {
5
                PORTB &= ~(1 << PB1); //Led geht an zum Test
6
                
7
                // hier sollen denn noch andere Sachen passieren
8
  }

Wenn aber das Wort "Test" in input_str vom Uart her kommt, geht es 
nicht. Vielleicht brauch ich auch mal ne Pause aber ich verstehs 
momentan nicht warum es so reagiert bzw. nicht reagiert...

Interessanterweise funktioniert es aber wenn ich auf einzelne Chars 
vergleiche z.B. ein kleines a:
1
if (strcmp (input_str, "Test") == 0)
2
  {
3
                PORTB &= ~(1 << PB1); //Led geht an zum Test
4
                
5
                // hier sollen denn noch andere Sachen passieren
6
  }

Hat da vielleicht jemand ne Idee oder sieht gleich was nicht stimmt, 
warum es mit einzelnen Zeichen geht aber mit ganzen Wörtern nicht? Das 
wär echt hilfreich ich weiß nämlich grad nicht mehr weiter :-)

Schöne Feiertage.

von Martin V. (oldmax)


Lesenswert?

Hi
Mit C kann ich dir nicht helfen, aber meine Kommunikation läuft immer 
über einen Ringpuffer. Dafür braucht es einen Schreib- und einen 
Lesezeiger auf einen Speicherblock. In C vermutlich ein Array mit Bytes. 
Die beiden Variablen werden beim Programmstart gleichgesetzt. Kommt ein 
Zeichen per Interrupt an, wird dieses in der ISR in die über den 
Schreibzeiger adressierte Speicherstelle geschrieben. Anschließend der 
Schreibzeiger erhöht und bei Erreichen der Arraygrenze der Schreibzeiger 
wieder auf 0 gesetzt. Mit ein paar Hilfsbits schreibe ich in der 
Hauptschleife die im Ringpuffer befindlichen Daten über der Lesezeiger 
in einen freigemeldeten Arbeitsspeicher. Ein Bit könnte signalisieren 
"Empfang läuft". Freigemeldet durch ein Ctrl-Bit nach der Bearbeitung 
eines vollständigen Telegramms. Natürlich wird der Lesezeiger dem 
Schreibzeiger nachgeführt. Bei der Datenübertragung in den 
Arbeitsspeicher wird ein Telegrammkopf, der zur Synchronisation dient, 
herausgefiltert und das erste folgende Byte mit der Anzahl der 
übertragenen Daten zur Prüfung des vollständigen Telegramme 
herangezogen. Durch den Ringpuffer bist du mit deinem Programm völlig 
unabhängig und du brauchst nicht irgendwo zu warten, bis Daten gelesen 
sind. Und ein Telegramm kann wie folgt aufgebaut sein
Kopf1  - bel.Zeichen
Kopf2 - bel. Zeichen
Kopf3 - bel. Zeichen
Anzahl Bytes im Telegramm, ohne Kopf, aber mit Anzahl-Byte
n - Daten
So läßt sich ein Telegramm gut auswerten.
Im Datenblock kann z.B. Absender, Empfänger, Auftrag und natürlich auch 
Werte stehen.
Sind alle Daten eingetroffen, setzt du dir ein Hit, Empfang vollständig. 
Nach Bearbeitung werden die Bits gelõscht und der Arbeitsspeicher 
freigegeben.
Vielleicht hilft es dir
Gruß oldmax

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Dann steht da nicht „Test“ im Array sondern noch etwas anderes.
Funktioniert strncmp mit Länge 4?

Wenn du mal den ganzen Code zeigen würdest - auch den für die Behandlung 
vom \n - könnte man dir helfen

Zeig nochmal den Vergleich für das a

: Bearbeitet durch User
von Westallgäuer E. (Gast)


Lesenswert?

Hallo,

danke oldmax für die Anleitung hilft mir gut weiter.

Dirk, mehr Code gibts nicht. In der letzten Sektion muß natürlich if 
(strcmp (input_str, „a“) == 0)

Deinen Hinweis mit strncmp probier ich mal aus danke.

von Westallgäuer (Gast)


Lesenswert?

Hoi zämä,

hab jetzt noch mal zwei Varianten ausprobiert, gehen beide auch nicht:
1
int8_t cmp = 0;
2
3
cmp = strncmp (input_str, "Test", 4);
4
5
     if (cmp == 0)
6
     {
7
       //mach die Led an
8
     }
1
if (strncmp (input_str, "Test", 4) == 0)
2
  {
3
    //mach die Led an
4
    input_str[0] = '\0';
5
  }

Hab jetzt noch ein bißchen rumexperimentiert...es funktioniert wenn ich 
sage wie unten, aber da wird ja nur das erste Zeichen geprüft, also 
frage ich eigentlich ja nur a ab. Schreibe ich statt 1 ne 2 in die 
Funktion und schicke ab rüber, gehts nicht mehr...
1
if (strncmp (input_str, "abc", 1) == 0)
2
  {
3
    //mach die Led an
4
    input_str[0] = '\0';
5
  }

Da ist wohl tatsächlich was Anderes wie "Test" im Array...naja ich schau 
mal morgen noch mal...danke für die Hilfe.

von Backslash-n (Gast)


Lesenswert?

vergleich mal mit "Test\n"

von foobar (Gast)


Lesenswert?

Strukturier das Programm etwas.  Und gib im Fehlerfalle auch einfach mal 
aus, was er versucht hat zu vergleichen.

Z.B. so (untested):
1
static char *
2
getline(char *buf, int size)
3
{
4
   int i = 0;
5
   char c;  // ignore status bits, UART_NO_DATA becomes '\0'
6
7
   assert(size > 0);
8
   // raw input - no echo, no line editing, truncate long lines:
9
   while ((c = uart_getc()) != '\n' && c != '\r')
10
      if (c && i < size-1)  // skip '\0' and leave space for one
11
         buf[i++] = c;
12
   buf[i] = 0;
13
14
   return buf;
15
}
16
17
main
18
   ...
19
   for (;;)
20
   {
21
      char buf[64];
22
23
      char *input = getline(buf, size(buf));
24
25
      // preprocess line, i.e. skip spaces, etc
26
      while (*input > 0 && *input <= ' ')
27
         input++;
28
29
      // handle commands
30
      if (strcmp(input, "version") == 0)
31
         uart_puts_P("V0.0\r\n");
32
      else if (strcmp(input, "hello") == 0)
33
         uart_puts_P("Hello World!\r\n");
34
      else if (*input)  // no error for empty lines
35
      {
36
         uart_puts_P("unknown command: '");
37
         uart_puts(input);
38
         uart_puts_P("'\r\n");
39
      }
40
   }

von Dirk B. (dirkb2)


Lesenswert?

Wird i denn jemals größer als 1?

Sende mal bei dem Vergleich mit „a“ ein „ba“

von Westallgäuer E. (Gast)


Lesenswert?

Guten Morgen,

@foobar:

Danke für den Hinweis, werde ich gleich mal machen.

Dirk B. schrieb:
> Wird i denn jemals größer als 1?

Meinst Du den Index zum Hochzählen? Also ich schicke zur Zeit maximal 5 
Zeichen..

> Sende mal bei dem Vergleich mit „a“ ein „ba“

Hm jetzt wirds lustig...ich vergleiche auf "a" und wenn ich "ba" schicke 
reagierts gleich wie wenn ich "a" schicke. Im zweiten strcmp genauso, da 
vergleiche ich auf "y" und wenn ich "xy" schicke reagierts auch beides 
Mal gleich.

Es schaut grad so aus als sucht der Vergleich immer bloß das kleine a 
bzw. das kleine y egal wo es ist...sobald ich aber auf mehr wie ein 
Zeichen vergleiche z.B. "ba" und das schicke geht nix mehr...irgendwo 
hab ich da einen Hund eingebaut...

von ach_herrje (Gast)


Lesenswert?

Westallgäuer E. schrieb:
> uint16_t incoming = 0;
> char input[6];
> char input_str[6];
> uint8_t i = 0;
>         if (incoming != '\n' && incoming != '\r')
>       {
>       input[i] = incoming;
>       i++;
>       input[i] = '\0';   //Nullbyte dazu
>         strcpy (input_str, input);  // strcpy (Ziel, Quelle)
>         i = 0;          // zurücksetzen für nächsten String
>         input[i] = '\0'; // leerer String
>
>       }
>

Da du nicht den ganzen Code schickst, wie man dir jetzt schon oefters 
gesagt hat, kann man nur raten. Aber, falls dein Code oben in einer 
Funktion steckt, setzt du vor der if-Abfrage i jedesmal auf 0, das 
heisst i wird nie hochgezaehlt und es steht immer nur ein Zeichen an der 
ersten Stelle.

von Stefan F. (Gast)


Lesenswert?

Wenn du keinen Debugger für den µC hast magst du die Auswertung der 
Zeichenketten vielleicht vorher mit einem PC Programm üben. Dort 
könntest du die serielle Eingabe durch die Tastatur ersetzen.

von Martin V. (oldmax)


Angehängte Dateien:

Lesenswert?

Hi
Ich hab mal in meinem Skizzenarchiv nachgesehen und eine Grafik zum 
Ringpuffer gefunden. Sie verdeutlicht die Vorgehensweise.
Der Vorteil eines Ringpuffers, der ca. 3- 4fache Größe eines Telegramms 
haben kann, du bist völlig unabhängig vom Empang der Daten.
Manchmal hat man Datenströme, die schnell hintereinander kommen, bevor 
wieder eine Pause eintritt. Der Puffer sollte so groß sein, das er diese 
Blöcke aufnehmen kann. Stellst du im Hauptprogramm fest, das Lesezeiger 
und Schreibzeiger unterschiedlich sind, nimmst du das erste Zeichen und 
vergleichst es mit dem 1. Zeichen deiner Telegrammkennung. Das geht 
relativ einfach, wenn du dir einen kleinen Speicherblock anlegst, in dem 
die Kennung hinterlegt ist. Dein Datenzähler adressiert nun die Kennung. 
Stimmt der Vergleich, dann zählst du den Datenzähler hoch, sonst behälst 
du den alten Wert. Hast du wie beim Beispiel bis 3 gezählt, dann stimmt 
der Kopf und du setzt dir das Flag, "Kopf ok" und du beginnst mit dem 
Datenzähler wieder bei 0. Diesen brauchst du nun, um die Anzahl der 
Daten zu erfassen und gleichzeitig deinen Arbeitsspeicher zu 
adressieren. Solange der Wert nun kleiner der Anzahl übertragener Bytes 
ist, schreibst du Daten in deinen Puffer, hast du die Anzahl erreicht, 
dann setzt du das Bit "Anzahl Daten erreicht". Damit sperrst du einen 
weiteren Datenübertrag und gibst gleichzeitig den Inhalt zur Bearbeitung 
frei. Nach Bearbeitung löscht du die Kontrollbits und gibst damit den 
Arbeitspuffer für weitere Daten wieder frei. Bei dieser Vorgehensweise 
ist eine unterschiedliche Datenmenge möglich. Wie bereits gesagt, kann 
ich dir in C nicht helfen, da ich C nicht beherrsche. Routinen für 
Assembler wären da kein Problem. Aber ich denke, mit der Skizze und den 
Hinweisen sollte es dir möglich sein, diese Vorgehensweise auch in C 
hinzubekommen.
Gruß oldmax

von Bernd (Gast)


Lesenswert?

Die Ringpuffergeschichte ist ja ganz nett, aber der TO kämpft doch noch 
mit viel grundlegenderen Problemen:
- erstmal muß eine komplette Zeichkette empfangen werden und
- diese muß sinnvoll/richtig ausgewertet werden

Der Ringpuffer ist doch dann das Sahnehäubchen. Und das wird auch nur 
dann gebraucht, wenn der Controller nebenbei noch was anderes machen 
soll, als auf Eingaben zu warten und diese auszuwerten.

von Martin V. (oldmax)


Lesenswert?

Hi
Nun, ich hab meine Erfahrung und ein Ringpuffer ist ja nun wirklich nix 
Erhabenes. Von mir aus kann er kämpfen bis zum Sanktnimmerleins Tag. Ich 
wollte ihm nur helfen, zu verstehen.

Bernd schrieb:
> Der Ringpuffer ist doch dann das Sahnehäubchen. Und das wird auch nur
> dann gebraucht, wenn der Controller nebenbei noch was anderes machen
> soll, als auf Eingaben zu warten und diese auszuwerten.

Tut er das nicht immer ?
Gruß oldmax

von foobar (Gast)


Lesenswert?

> Nun, ich hab meine Erfahrung und ein Ringpuffer ist ja nun wirklich nix
> Erhabenes. Von mir aus kann er kämpfen bis zum Sanktnimmerleins Tag. Ich
> wollte ihm nur helfen, zu verstehen.

Nur hilft ihm ein Ringpuffer überhaupt nicht.  In Fleurys UART-Routinen 
befinden sich bereits zwei - da sind sie angebracht.  Aber nicht bei dem 
Problem des TO.

von Dirk B. (dirkb2)


Lesenswert?

Westallgäuer E. schrieb:
> Dirk B. schrieb:
>> Wird i denn jemals größer als 1?
>
> Meinst Du den Index zum Hochzählen? Also ich schicke zur Zeit maximal 5
> Zeichen..
>
>> Sende mal bei dem Vergleich mit „a“ ein „ba“
>
> Hm jetzt wirds lustig...ich vergleiche auf "a" und wenn ich "ba" schicke
> reagierts gleich wie wenn ich "a" schicke.

Das deutet darauf hin, dass die Eingabe immer nur beim Index [0] 
gespeichert wird.

Entweder änderst du i nicht oder es wir immer wieder auf 0 gesetzt.

Könnte man sehr leicht beurteilen, wenn man den Code hat.

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Westallgäuer E. schrieb:
> Aus einem mir bisher unbekannten Grund wird der Code in den {} oben nie
> ausgeführt.

Tja, dann denke mal drüber nach, wann genau die Bedingung wahr werden 
kann.

Ich hänge dir mal was zum Lesen und Verstehen dran. Bedenke mal, daß es 
imer besser ist, low-level Zeugs zu trennen von Algorithmen, also 
high-level Zeugs. Strukturiere also deine Projekte, so daß du ein jedes 
separat austesten kannst und dich dann drauf verlassen kannst.

W.S.

von W.S. (Gast)


Lesenswert?

Martin V. schrieb:
> Ich hab mal in meinem Skizzenarchiv nachgesehen und eine Grafik zum
> Ringpuffer gefunden. Sie verdeutlicht die Vorgehensweise.

Ein Ringpuffer ist zum Zwischenspeichern innerhalb des Lowlevel-Treibers 
geeignet, aber dort soll er gefälligst auch bleiben, ohne daß 
übergeordnete Programmteile auf ihn einen Zugriff haben. Stattdessen 
sollte es nur 2 Abfragefunktionen geben:
bool IsCharAvailable(void);
und
char GetChar(void);

Für das Handhaben von Kommandozeilen macht man das anders. Dort ist ein 
Ringpuffer sinnlos, da man ja nach Abarbeiten des Kommandos ohne 
jeglichen Aufwand die Kommandozeile löschen kann.

W.S.

von c-hater (Gast)


Lesenswert?

W.S. schrieb:

> Ein Ringpuffer ist zum Zwischenspeichern innerhalb des Lowlevel-Treibers
> geeignet, aber dort soll er gefälligst auch bleiben, ohne daß
> übergeordnete Programmteile auf ihn einen Zugriff haben.

Das ist doch Unsinn. Das zwingt de facto zu (völlig nutzlosem) 
Umkopieren.

> Stattdessen
> sollte es nur 2 Abfragefunktionen geben:
> bool IsCharAvailable(void);
> und
> char GetChar(void);

Also ich habe da zwei andere Funktionen (in der Realität natürlich oft 
in Assembler, aber ich schreibe es mal C-isch, damit du es verstehen 
kannst):

uint8_t CharsInBuffer(void);
und
char GetChar(uint8_t Index);

Damit kann ich alles das machen, was du mit deinen zwei Funktionen auch 
machen kannst, aber ich kann auch noch sehr viel mehr damit machen, ohne 
die Notwendigkeit, den Mist in einen weiteren Buffer umkopieren zu 
müssen. Die nützlichen Eigenschaften des Ringpuffers werden von diesen 
erweiterten Möglichkeiten des Zugriffs von der Empfängerseite übrigens 
in keinster Weise beeinträchtigt.

> Für das Handhaben von Kommandozeilen macht man das anders. Dort ist ein
> Ringpuffer sinnlos, da man ja nach Abarbeiten des Kommandos ohne
> jeglichen Aufwand die Kommandozeile löschen kann.

Harhar. Das kann nur jemand vertellen, der noch nie seinen Code gegen 
einen automatisierten Peer getestet hat. Kleiner Hinweis: sowas kann 
Kommandozeilen ggf. extrem schnell absondern und nicht immer ist im 
Protokoll ein Feedback oder Prompt vorgesehen, was den Peer in seinem 
Elan bremsen würde...

von Zeno (Gast)


Lesenswert?

Jetzt bin ich auf die Antwort von W.S. gespannt.

Der Thread hat jetzt durchaus das Potenzial interessant zu werden.

von c-hater (Gast)


Lesenswert?

Zeno schrieb:
> Jetzt bin ich auf die Antwort von W.S. gespannt.
>
> Der Thread hat jetzt durchaus das Potenzial interessant zu werden.

Naja, ich hoffe, es würde ihm auffallen, dass da natürlich noch eine 
Funktion fehlt. Die liefere ich hier nach:

void AcceptChars(uint8_t count);

von Martin V. (oldmax)


Lesenswert?

Hi
Manchmal verstehe ich euch nicht. Jeder, der programmieren kann, weiß 
doch, was ein Ringpuffer ist und wie man mit ihm umgeht. Für einen 
Anfänger aber ist oft allein das Wort ein Hexenwerk und damit dieser 
Irrglaube ein wenig endet, habe ich versucht, dem TO mit einer Grafik 
die Funktion zu erklären. Sein Problem ist scheinbar in der 
Adressierung, denn so wie es aussieht, funktioniert Senden und 
Empfangen. Lediglich die Inhalte sind nicht wie erwartet. Mein Beitrag 
sollte ihm helfen, die Daten adressmäßig zuzuordnen. Und das es dazu 
globale Adresszeiger braucht. Sicherlich ist dieser auch in einer 
Hochsprache, sofern man C dazu zählen kann, auch in einer Funktion zu 
übergeben, aber er muß im Prinzip schon irgendwo global verfügbar und 
gültig sein.
Wenn er einen Adresszeiger in einer Funktion aufruft, ist dieser, wenn 
er lokal vereinbart ist, halt immer 0. Ob das der Grund für das 
Fehlverhalten ist, können wir aufgrund fehlender Information aber nicht 
wissen und darum hab ich die Skizze ins Forum gestellt. Nicht für euch 
Experten. Das maß ich mir nicht an, euch zu belehren. Aber ein Anfänger 
sollte mit dieser Hilfe seinen Fehler finden oder vielleicht die gesamte 
vorherige Vorgehensweise neu aufsetzen können.
Also, diskutiert nicht darüber, was und wozu irgendwelche Puffer sind. 
Das in der Skizze der Vorschlag enthalten ist, Daten aus dem Ringpuffer 
in einen Arbeitspuffer zu kopieren, ist der besseren Übersicht 
geschuldet. Natürlich kann man den sparen und direkt die Daten im 
Ringpuffer auswerten, machts aber nicht leichter.
Ein schönes Osterfest wünsch ich euch.
Gruß oldmax

von Westallgäuer (Gast)


Lesenswert?

Hallo zusammen,

ich werd da der Sache mal nachgehen und schauen ob ich mit Euren Tipps 
und Hinweisen da was hinkrieg.

Ich melde mich.

Gruß und schöne Woche.

von Westallgäuer (Gast)


Lesenswert?

Servus mitanand,

also ich wollt bloß kurz Bescheid sagen jetzt funktionierts...danke für 
Eure Hilfe. Jetzt muß ich bloß noch gucken wie ich meinen String mit 
strtok auftrennen kann und denn die nach dem : empfangenen Werte auf 
zwei verschiedene Variablen verteilen kann...und mal gucken ob es nicht 
irgendwo sich selbere blockiert...aber wird schon schiefgehen ;-)

Vielen Dank nochmal und schönes Wochenende

von Dirk B. (dirkb2)


Lesenswert?

strtok verändert den String (indem es die Delimiter mit '\0' 
überschreibt)
sscanf kann da viel besser funktionieren
oder strchr und strtol (oder ähnliche Funktion)

von W.S. (Gast)


Lesenswert?

Dirk B. schrieb:
> strtok verändert den String...

Die ganze Herangehensweise ist mMn ausgesprochen ungünstig, man könnte 
dazu auch falsch sagen. Ich würde das Ganze in 3 Ebenen aufteilen:
1.Ebene: (=low level) Behandlung des Zeichenempfanges als solchen.
2.Ebene: Auflaufen einer Kommandozeile in einen Kommandostring (ASCIIZ)
3.Ebene: Auswertung der Kommandozeile ohne sowas wie strtok usw.

Dabei ist das Erkennen eines Kommandos eigentlich recht einfach:
1
char match (char* item, char** Zptr)
2
{ char* P;
3
  IgnoreSpace(Zptr);
4
  P = *Zptr;
5
  if (UpCase(*P) != *item) return 0;
6
  while (*item) { if (UpCase(*P++)!=(*item++)) return 0; }
7
  *Zptr = P;
8
  IgnoreSpace(Zptr);
9
  return 1;
10
}

Und wie man ein IgnoreSpace oder ein UpCase schreibt, sollte bekannt 
sein.

W.S.

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Und wie man ein IgnoreSpace oder ein UpCase schreibt, sollte bekannt
> sein.

Die spannende Frage ist, was macht IgnoreSpace()? Doch nicht etwas den 
Eingabestring verändern?

von W.S. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Die spannende Frage ist...

Nö, es gibt hier gar keine spannenden Fragen. Zum Auswerten wird einfach 
ein Zeiger auf den Zeilenanfang gesetzt und zum Übergehen von white 
space inkrementiert bis er auf ein nicht-white-space Zeichen oder die 
abschließende 0 kommt. Match funktioniert ähnlich: es setzt den Zeiger 
hinter den erkannten Token und läßt ihn bei unerkanntem Token dort 
stehen, wo er ist.

W.S.

von W.S. (Gast)


Lesenswert?

Nachtrag:
Weil einem sowas immer wieder vorkommt, hatte ich das Ganze hier schon 
mal gepostet, also das komplette Kommandoprogramm incl. einer simplen 
Korrektur für die Eingabe (BKSP löscht das zuletzt eingegebene Zeichen). 
Das reicht für die allermeisten Fälle aus.

W.S.

von Peter D. (peda)


Lesenswert?

Hier mal ein kleines Beispiel:
1
void execute(char* buf)
2
{
3
  cmd_data.answer = buf;                                // answer overwrite command
4
  cmd_data.args = sscanf_P(buf, PSTR(CMD_FMT CMD_FMT "%g"), 
5
                           cmd_data.cmd, cmd_data.par, &cmd_data.val);
6
  if (cmd_data.args != (uint8_t)EOF)                    // if not empty
7
  {
8
    for (command_t* cmd_p = (command_t*)cmd_table; pgm_read_byte(cmd_p->cmd) != '\0'; cmd_p++)
9
    {
10
      if (strcmp_P(cmd_data.cmd, cmd_p->cmd) == 0)
11
      {
12
        func f_ptr = (func)pgm_read_word(&cmd_p->func);
13
        f_ptr();
14
        return;
15
      }
16
    }
17
  }
18
  answer_err(CMDERR_UNKNOWN_CMD);                       // command unknown
19
}

sscanf zerlegt die Zeile in 2 Strings und einen Wert.
Dann wird der erste String mit der Kommandoliste verglichen und bei 
Match der Funktionspointer in der Liste ausgeführt.
Die Liste endet mit einem leeren String (""). Damit kann man beliebig 
Einträge hinzufügen.
CMD_FMT wird so definiert, daß die Stringpuffer nicht überlaufen können, 
z.B.
1
#define CMD_FMT "%16s" // for array size 17
Soll die Schreibweise egal sein, nimmt man statt strcmp das strcasecmp.

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.