Forum: Mikrocontroller und Digitale Elektronik Verzweifle daren einen String mit UART einzulesen


von mws (Gast)


Lesenswert?

Hallo Gemeinde,

ich hab da mal ein Problem, bei dem ich langsam verzweifle.

Ich möchte einen Komandoparser in meinen Mega32 bauen und dazu zunächst 
einmal einen String über die ser. Schnittstelle einlesen. Ich verwende 
Peter Fleury's uart.c um die Daten zu senden bzw zu empfangen.

Da in seiner Lib keine funktion enthalten ist um Strings zu lesen habe 
ich folgende Routine hinzugefügt:
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include <avr/pgmspace.h>
7
8
#include "uart.h"
9
10
/* define CPU frequency in Mhz here if not defined in Makefile */
11
#ifndef F_CPU
12
#define F_CPU 16000000UL
13
#endif
14
15
/* 38400 baud */
16
#define UART_BAUD_RATE      38400      
17
18
19
void uart_gets( char* Input )
20
{
21
  char c = uart_getc();
22
  uart_putc( c );   // und gleich wieder zurückschicken, damit
23
                    // der Benutzer auch was sieht
24
25
  while( c != '\r' ) {
26
    *Input = c;
27
    Input++;
28
    c = uart_getc();
29
    uart_putc( c );
30
  }
31
  *Input = '\0';
32
}
33
34
int main()
35
{
36
37
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
38
  
39
  sei();
40
41
  uart_puts("Hier geht es los\r\n>>");
42
43
  char Buffer[50];
44
45
  while( 1 ) {
46
47
    uart_gets( Buffer, 50);   // Auf eine Zeile vom Benutzer warten
48
49
    //
50
    // Testweise die komplette Zeile zurückschicken, so wie
51
    // sie vom µC empfangen wurde
52
    //
53
    uart_puts( "Ich habe verstanden : " );
54
    uart_puts( Buffer );
55
    uart_putc( '\n' );
56
57
    // war die Eingabe vom Benutzer der Text 'Test'
58
    if( strcmp( Buffer, "Test" ) == 0 ) {
59
      // Wenn ja, dann begrüsse ihn
60
      uart_puts( "Hallo Test\n" );
61
      uart_puts( "Wie gehts\n" );
62
    }
63
  }
64
}

Leider funktioniert das nicht so wie gedacht sondern es passieren zwei 
Dinge:

1. der Prompt wird angezeigt:
Hier gehts los
>>

2. bei der Eingabe werden die Buchstaben im Terminalfenster ausgegeben. 
Wenn ich Enter drücke springt die Anzeige an den Anfang der Zeile. Aber 
es wird nichts ausgegeben.

3. Wenn ich erneut Buchstaben eingebe werden diese wieder im Terminal 
dargestellt. Wenn ich dann zum zweiten Mal Enter drücke scheint sich der 
Controller zu reseten, denn ich bekomme wieder den Prompt
Hier geht es los
>>
angezeigt.

Kann mir mal einer einen Tipp geben, was ich da ggf. falsch mache?

Es macht übrigens auch keinen untershcied, ob ich mit Int arbeite 
(welches in der lib ja im oberen Byte eventuelle Fehler anzeigt) und den 
unteren Teil für den String nutze oder ob ich (wie oben) direkt mit char 
arbeite.

Gruß

mws

von MWS (Gast)


Lesenswert?

He, he, hallo mws :D

Du hast das hier durchgearbeitet ?

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART#einzelne_Zeichen_empfangen

Denk' mal es ist nicht so perfekt, void uart_gets( char* Input ) mit 
einem Argument  zu definieren, aber mit 2 Argumenten aufzurufen:

uart_gets( Buffer, 50);

Gibt's da nicht Mecker vom Compiler ?

Interessant ist auch laut Tutorial:"...daher mit einem Return ('\n') 
abgeschlossen wird."

Was hat dann das hier in Deinem Quelltext zu bedeuten: while( c != '\r' 
)

von Ben _. (burning_silicon)


Lesenswert?

wohl weil eigentlich immer CR+LF gesendet wird... \r\n oder ascii 13,10

von Michael S. (mws)


Lesenswert?

Hi,

also ich hab die Variante mit dem MAXLEN natürlich auch ausprobiert aber 
wenn ich die verwende wartet wie Endlosschleife in main nicht bis ein 
kompletter String eingegeben ist sondern es läuft immer die "Ich habe 
verstanden:" Anzeige durch und man kann gar nichts eingeben.

Das ich auf '\r' prüfen muss weil mein Terminalprogramm scheinbar kein 
'\n' sendet hat auch ne Weile gedauert.

Das Gleiche Problem tritt aber auch auf, wenn ich als Zeilenende z.B das 
Semikolen verwende. :-(

mws

von Karl H. (kbuchegg)


Lesenswert?

mws schrieb:
> Hallo Gemeinde,
>
> ich hab da mal ein Problem, bei dem ich langsam verzweifle.
>
> Ich möchte einen Komandoparser in meinen Mega32 bauen und dazu zunächst
> einmal einen String über die ser. Schnittstelle einlesen. Ich verwende
> Peter Fleury's uart.c um die Daten zu senden bzw zu empfangen.

Dann sieh dir das Demo vom Peter an oder lies dir wenigstens die Doku 
dazu durch.
uart_getc ist kein wartendes getc sondern kehrt auch dann zurück, wenn 
kein Zeichen vorliegt.

von Michael S. (mws)


Lesenswert?

Hi,

ich hätte vielleicht nicht bis 3 Uhr hantieren sollen :-)
1
uart_getc ist kein wartendes getc sondern kehrt auch dann zurück, wenn
2
kein Zeichen vorliegt.
Genau das ist das "Problem" gewesen.

Ich hab die Lösung, die man mit Peters Lib benötigt mal angehängt:
1
/*************************************************************************
2
Function: uart_gets()
3
Purpose:  return array of char
4
Input:    Pointer to the array
5
      Len of the Array
6
      The charater used for end character
7
Returns:  none
8
**************************************************************************/
9
void uart_gets( char* input, char maxlen, char cr )
10
{
11
  unsigned int c;
12
  unsigned char strlen = 0;
13
14
    for(;;)
15
    {
16
        /*
17
         * Get received character from ringbuffer
18
         * uart_getc() returns in the lower byte the received character and 
19
         * in the higher byte (bitmask) the last receive error
20
         * UART_NO_DATA is returned when no data is available.
21
         *
22
         */
23
        c = uart_getc();
24
        if ( c & UART_NO_DATA )
25
        {
26
            /* 
27
             * no data available from UART 
28
             */
29
        }
30
        else
31
        {
32
            /*
33
             * new data available from UART
34
             * check for Frame or Overrun error
35
             */
36
            if ( c & UART_FRAME_ERROR )
37
            {
38
                /* Framing Error detected, i.e no stop bit detected */
39
                uart_puts_P("UART Frame Error: ");
40
            }
41
            if ( c & UART_OVERRUN_ERROR )
42
            {
43
                /* 
44
                 * Overrun, a character already present in the UART UDR register was 
45
                 * not read by the interrupt handler before the next character arrived,
46
                 * one or more received characters have been dropped
47
                 */
48
                uart_puts_P("UART Overrun Error: ");
49
            }
50
            if ( c & UART_BUFFER_OVERFLOW )
51
            {
52
                /* 
53
                 * We are not reading the receive buffer fast enough,
54
                 * one or more received character have been dropped 
55
                 */
56
                uart_puts_P("Buffer overflow error: ");
57
            }
58
59
            uart_putc( c );   // und gleich wieder zurückschicken, damit
60
                    // der Benutzer auch was sieht
61
      
62
            if (c != cr && strlen <= maxlen-1)
63
            {
64
               strlen++;
65
               *input = c;
66
               input++;
67
            }
68
            else
69
           {
70
               *input='\0';
71
               return;
72
          }
73
       }
74
   }
75
}/* uart_gets */

von Purzel H. (hacky)


Lesenswert?

Je nachdem woher ein String kommt, kann man sich's einfacher machen 
anstelle eines Null-terminierten Strings einen mit der Laenge am Anfang 
zu verwenden.

von Ben _. (burning_silicon)


Lesenswert?

dabei gibts dann wieder andere probleme, z.b. wenn während der 
übertragung ein zeichen verloren geht. ich bevorzuge bei textübertragung 
daher die variante mit endmarkierung. das gibt zwar auch probleme wenn 
die endmarkierung verloren geht, aber wenn du 200 zeichen überträgst 
(davon 199 zeichen nutzdaten, eines länge oder endmarke) ist bei der 
endmarkierung-lösung nur das letzte zeichen kritisch. der controller 
kann auch ggf. sofort anhand einer prüfsumme feststellen, daß es einen 
übertragungsfehler gab, während man bei einer vorgegebenen länge erst 
auf ein timeout warten muß. großer nachteil: funktioniert nicht bei 
unbekannten binären daten, weil das als endmarkierung verwendete zeichen 
hier auch in den nutzdaten vorkommen kann. ohne umkodierung (entfernen 
des endmarkierungs-zeichen aus den nutzdaten) gehts hier nur mit 
bekannter länge.

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.