Forum: Mikrocontroller und Digitale Elektronik Fleury's Uart, wie string empfangen?


von ProblemBär (Gast)


Lesenswert?

Hallo Leute,

ich will mit der Uart Lib von Fleury einen String vom PC zum AVR 
schicken. Einzelne Zeichen Funktionieren ach wunderbar, aber ich möchte, 
dass alle Zeichen erst in einen char Array geschrieben werden und das 
dann beispielsweise komplett zurückgegeben wird ( Später natürlich eine 
andere Anwendung). Wie muss ich denn die for-Schleife erweitern, dass 
erst der komplette String ausgegeben wird?
1
int main(void)
2
{
3
    unsigned int c;
4
  char test[20];
5
  int i;
6
  
7
  /*
8
     *  Initialize UART library, pass baudrate and AVR cpu clock
9
     *  with the macro 
10
     *  UART_BAUD_SELECT() (normal speed mode )
11
     *  or 
12
     *  UART_BAUD_SELECT_DOUBLE_SPEED() ( double speed mode)
13
     */
14
    uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); 
15
    
16
    /*
17
     * now enable interrupt, since UART library is interrupt controlled
18
     */
19
    sei();
20
    
21
    /*
22
     *  Transmit string to UART
23
     *  The string is buffered by the uart library in a circular buffer
24
     *  and one character at a time is transmitted to the UART using interrupts.
25
     *  uart_puts() blocks if it can not write the whole string to the circular 
26
     *  buffer
27
     */
28
    //uart_putc('\r');
29
  i=0;
30
    for(;;)
31
    {  
32
        /*
33
         * Get received character from ringbuffer
34
         * uart_getc() returns in the lower byte the received character and 
35
         * in the higher byte (bitmask) the last receive error
36
         * UART_NO_DATA is returned when no data is available.
37
         */
38
        c = uart_getc();
39
        if ( c & UART_NO_DATA )
40
        {
41
            /* 
42
             * no data available from UART 
43
             */
44
        }
45
        else
46
        {
47
            /*
48
             * new data available from UART
49
             * check for Frame or Overrun error
50
             */
51
            if ( c & UART_FRAME_ERROR )
52
            {
53
                /* Framing Error detected, i.e no stop bit detected */
54
                uart_puts_P("UART Frame Error: ");
55
            }
56
            if ( c & UART_OVERRUN_ERROR )
57
            {
58
                /* 
59
                 * Overrun, a character already present in the UART UDR register was 
60
                 * not read by the interrupt handler before the next character arrived,
61
                 * one or more received characters have been dropped
62
                 */
63
                uart_puts_P("UART Overrun Error: ");
64
            }
65
            if ( c & UART_BUFFER_OVERFLOW )
66
            {
67
                /* 
68
                 * We are not reading the receive buffer fast enough,
69
                 * one or more received character have been dropped 
70
                 */
71
                uart_puts_P("Buffer overflow error: ");
72
            }
73
            /* 
74
             * send received character back
75
             */
76
      test[i] = c;
77
      i++;
78
      uart_puts(test);
79
80
81
82
83
        }
84
    }
85
    
86
}

Irgendwie hab ich voll den Knoten im Hirn und komm nich weiter:(

von ProblemBär (Gast)


Lesenswert?

hat keiner ne idee?

von Karl H. (kbuchegg)


Lesenswert?

Ist doch schon mal ein schöner Ansatz.

Du solltest dir zur Gewohnheit machen, für in sich abgeschlossene
Aufgaben eine eigene Funktion zu machen.

In deinem Fall lautet die Aufgabe: Empfange einen String, wobei
auf den String gewartet werden kann.

Soweit so gut. Aber: Woher weist du eigentlich, dass ein String
zu Ende ist. Nur weil bei einem Aufruf von uart_getc() kein
Character zurückkommt, heist das ja nicht, das deswegen der
String zu Ende ist. Es könnte ja auch sein, dass der Benutzer
am anderen Ende einfach nur langsamer tippt.
-> Du musst dich für eine Strategie entscheiden, mit der
du zweifelsfrei erkennen kannst, dass der String jetzt zu
Ende ist.
Oft wird dazu der Empfang des Zeichens '\n' (Return) benutzt.
Für viele Benutzer ist es mitlerweile zur zweiten Natur geworden
eine Eingabe durch Druck auf 'Return' zu beenden. Und genau
das ist dann für deine Funktion das Kennzeichen, dass der String
zu Ende ist.

Was muss deine Funktion also machen? Formulier das mal
umgangssprachlich

  es muss irgendwo ein character Array geben, in dem
  empfangene Zeichen zwischengespeichert werden können um
  den C-String aus den einzelnen Zeichen aufzubauen

  so muss in einer Schleife auf jeweils den Empfang eines
  Zeichens warten

  War das Zeichen ein '\n', dann ist der Text komplett
  übertragen worden. In guter C-Manier, wird an den bisherigen
  Text im character Array noch ein '\0' angehängt um daraus einen
  gültigen C-String zu machen und die Funktion ist zu Ende.

  War das Zeichen kein '\n', dann wird es ganz einfach an den
  bisherigen Text im character Array angehängt. Das erfordert
  allerdings, dass man weiss, wo denn das letzte Zeichen im
  Array eingefügt wurde -> man braucht eine Variable dafür.

  Je nach Bedarf ist es auch eine gute Idee, das Zeichen über
  die UART wieder zurück zu schicken, damit der Benutzer auch
  sieht was er tippt.

  Wenn du willst, kannst du auch rudimentäre Edit-Funktionalität
  einbauen, um zb. dem Benutzer die Möglichkeit zu geben, mittels
  Backspace wieder zurückzugehen:
     Wird ein '\b' empfangen, so verminderst du die Textzählung um 1
     und gibst die Sequenz '\b', ein Leerzeichen und noch ein '\b'
     aus.

  Wie auch immer, es geht auf jeden Fall in der Schleife innerhalb
  der Funktion weiter. Diese Schleife innerhalb der Funktion kann
  nur durch den Empfang eines '\n' verlassen werden.

Tja. Das wars dann auch schon. Damit hast du eine Funktion, die
auf den Erhalt eines Textes (einer Eingabezeile) wartet. Und die
verwurschtest du dann in deiner main() Schleife weiter.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Den String solltest du immer mit einem Nullbyte abschliessen.

      test[i] = c;
      i++;
      test[i] = '\0'; // <===

Du solltest nie mehr Zeichen in das Array test reinschreiben als Platz 
reserviert ist.

    if (i < (20-1)) // -1 wg. abschliessendem Nullbyte
    {
      test[i] = c;
      i++;
      test[i] = '\0'; // <===
    }

Im Moment gibst du bei jedem empfangenen Zeichen den String in test aus, 
so weit er bereit gefüllt ist. Schlauer wäre es, den String erst 
auszugeben, wenn das Zeilenendezeichen empfangen wurde oder der String 
"voll" ist.

    if (c == '\n' || i == (20-1))
    {
      uart_puts(test);
      i = 0;          // zurücksetzen für nächsten String
      test[i] = '\0'; // leerer String
    }

von ProblemBär (Gast)


Lesenswert?

wow danke, echt nett von euch, ich glaub mit den super tipps werd ichs 
hinkriegen;)

von ProblemBär (Gast)


Lesenswert?

eine frage hätt ich dann doch noch. hab das ganze jetzt mal in meinen 
code eingebaut und es funktioniert auch...aber leider immer nur einmal. 
Ich gebe einen String im terminal ein, dieser wird wie er soll 
verarbeitet, und auch wieder ausgegeben. gebe ich dann den nächsten 
string ein, so wird er nicht mehr verarbeitet, jedoch noch ausgegeben. 
bei der nächsten eingabe passiert dann garnichts mehr:( nicht über die 
merkwürdigen umrechnungen und hin und herschiebereien das wundern, das 
wird alles noch vereinfacht^^ aber wo liegt das problem, dass das 
programm nicht bei jedem neu eingebenen String normal weiterarbeitet? ( 
Die eingegeben Strings haben immer die Länge 64)

von ProblemBär (Gast)


Angehängte Dateien:

Lesenswert?

oh, hier noch der code

von ProblemBär (Gast)


Lesenswert?

ok, problem gelöst, die spi_init musste in aus der if bedingung 
raus...danke nochmal

von Maik (Gast)


Lesenswert?

Hallo ich möchte jetzt auch nochmal auf die Thematik UART Lib und String 
empfangen zurück kommen.

Ich verwende den Mega32 jedoch ist es mir nicht möglich einen String zu 
empfangen und anschließend auszugeben.
Einzelne Zeichen zu empfangen und senden geht fehlerfrei.

Ich habe dabei die UART LIB von Peter Fleury mit Interupts verwendet und 
zudem den Quellcode aus dem AVR-GCC Tutorial.

Ich würde mich über Tipps freuen.
1
/* Include Dateien */
2
#include <stdlib.h>
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <avr/signal.h>
6
#include <avr/pgmspace.h>
7
8
#include "uart.h"
9
10
/* CPU Frequenz definieren */
11
#define F_CPU 16000000UL
12
13
/* Baudrate 19200 */
14
#define UART_BAUD_RATE      19200
15
16
void uart_gets( char* Buffer, uint8_t MaxLen )
17
{
18
  uint8_t NextChar;
19
  uint8_t StringLen = 0;
20
21
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
22
23
                                  // Sammle solange Zeichen, bis:
24
                                  // * entweder das String Ende Zeichen kam
25
                                  // * oder das aufnehmende Array voll ist
26
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
27
    *Buffer++ = NextChar;
28
    StringLen++;
29
    NextChar = uart_getc();
30
  }
31
32
                                  // Noch ein '\0' anhängen um einen Standard
33
                                  // C-String daraus zu machen
34
  *Buffer = '\0';
35
}
36
37
38
/* main */
39
int main(void)
40
{
41
  unsigned int c;
42
  char Line[40];      // String mit maximal 39 zeichen
43
44
    /* UART initialisieren*/
45
    uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
46
    /* Interupts freigeben*/
47
    sei();
48
49
    for(;;)
50
        {
51
            // Empfangenes Zeichen speichern
52
            c = uart_getc();
53
            if ( c & UART_NO_DATA )
54
            {
55
                 //Keine Daten vorhanden
56
            }
57
            else
58
            {
59
                 //Neue Daten sind vorhanden
60
                 //Überprüfung auf Fehler
61
                if ( c & UART_FRAME_ERROR )
62
                {
63
                    //FRAMING ERROR festgestellt, z.B. kein Stop Bit vorhanden
64
                    //uart_puts_P("UART Frame Error: ");
65
                }
66
                if ( c & UART_OVERRUN_ERROR )
67
                {
68
                    //OVERRUN ERROR festgestellt, z.B. zuvor empfangenes Zeichen konnte
69
                    //noch nicht verarbeitet werden -> Datenverlust
70
                    uart_puts_P("UART Overrun Error: ");
71
                }
72
                if ( c & UART_BUFFER_OVERFLOW )
73
                {
74
                  //BUFFER OVERFLOW festgestellt, z.B. zuvor empfangenes Zeichen konnte
75
                    //noch nicht gelesen werden -> Datenverlust
76
                  uart_puts_P("Buffer overflow error: ");
77
                }
78
                 //Empfangenes Zeichen verarbeiten
79
                uart_putc( (unsigned char)c );
80
                uart_gets( Line, sizeof( Line ) );
81
                uart_putc( (unsigned char) sizeof( Line ) );
82
                uart_puts( Line );
83
            }
84
        }
85
}

Gruß
Maik

von Stefan E. (sternst)


Lesenswert?

1
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen

Dein Problem ist, dass die Funktion uart_getc der Fleury-Lib eben 
nicht auf ein Zeichen wartet.

von Maik (Gast)


Lesenswert?

Mh okay da ist etwas dran.
Jedoch dachte ich ist diese Lib Interuptgesteuert?
Oder irre ich da?

Wenn Nein, dann würde mich interessieren ob und wie ich diese Lib 
anpassen muss oder ob ich dann selber mir ne funktion uart_getc() 
schreiben muss....

Denn eigentlich ist diese Lib ja schon ganz net.

von Stefan E. (sternst)


Lesenswert?

Maik wrote:

> Jedoch dachte ich ist diese Lib Interuptgesteuert?

Eben, deshalb wartet die Funktion ja auch nicht. Wenn die Funktion immer 
so lange warten würde, bis ein Zeichen da ist, welchen Sinn hätte dann 
der Interrupt gesteuerte Empfang?

> Wenn Nein, dann würde mich interessieren ob und wie ich diese Lib
> anpassen muss oder ob ich dann selber mir ne funktion uart_getc()
> schreiben muss....

Es wäre sinnvoller, die Funktion uart_gets anzupassen.

von Karl H. (kbuchegg)


Lesenswert?

Maik wrote:

> Wenn Nein, dann würde mich interessieren ob und wie ich diese Lib
> anpassen muss oder ob ich dann selber mir ne funktion uart_getc()
> schreiben muss....

Wozu?
Das lustige ist:
In deiner main() hast du ein perfektes Beispiel dafür, wie man uart_getc 
aus der Fleury Lib korrekt einsetzt.

Vielleicht einfach mal die Beispiele, die mit der Lib kommen, studieren 
und abklären ob deine Vorstellungen darüber wie die Lib zu benutzen ist 
richtig sind, ehe man drangeht eine gut funktionierende Lib 
umzuschrieben :-)

von Maik (Gast)


Lesenswert?

So jetzt hab ich mich nochmal mit der Lib beschäftigt.
Die empfangen Zeichen hängen ja in einem Ringpuffer und die uart_getc 
holt sich die zeichen aus diesem puffer. Dies geschieht ja bei jedem 
Durchlauf der FOR-Schleife im main. So und dann wird ja auch direkt 
überprüft ob jetzt ein Zeichen da ist oder ein Fehler aufgetreten ist 
oder nicht.
Soweit so gut.

Wenn ich also jetzt einen String empfangen möchte muss ich mich ans Ende 
der ELSE Funktion hängen, da dann ja gewährleistet ist, das ein Zeichen 
korreckt empfangen wurde und verfügbar ist zur weiterverarbeitung.

jetzt habe ich dann meine uart_gets() Funktion dahingehend umgebaut.
Also diese While schleife weg und durch if ersetzt.
Hier jetzt nochmal der Teil in der main
1
...
2
               //Empfangenes Zeichen verarbeiten
3
                uart_gets( Line, sizeof( Line ), (unsigned char)c );
4
5
            }
6
            if (flag == 1)
7
            {
8
        flag=0;
9
        //uart_puts(Line);
10
        uint8_t i=0;
11
12
        while (i < sizeof (Line))
13
        {
14
          uart_putc(Line[i]);
15
          i++;
16
        }
17
            }
18
        }
19
}

und hier die Funktion uart_gets()
1
void uart_gets( char* Buffer, uint8_t MaxLen, unsigned char c)
2
{
3
  uint8_t StringLen = 0;
4
5
                  // Warte auf und empfange das nächste Zeichen
6
7
                  // Sammle solange Zeichen, bis:
8
                  // * entweder das String Ende Zeichen kam
9
                  // * oder das aufnehmende Array voll ist
10
  if( c != '\n' && StringLen < MaxLen - 1 )
11
  {
12
    *Buffer++ = c;
13
    StringLen++;
14
    //uart_putc((unsigned char)c );
15
    //uart_puts("\n");
16
    //uart_puts("Zeichen empfangen\n");
17
18
  }
19
  if( c == '\n')
20
  {
21
                    // Noch ein '\0' anhängen um einen Standard
22
                    // C-String daraus zu machen
23
      *Buffer = '\0';
24
      flag=1;
25
      //uart_putc((unsigned char)c );
26
      //uart_puts("\n");
27
      //uart_puts("Ende des Strings\n");
28
  }
29
30
}

Soweit ich das jetzt testen konnte führt er diese funktion auch richtig 
aus.
Jedoch habe ich das gefühl, das der string nicht richtig in das Array 
LINE wandert.
Ebenso glaube ich hab ich noch nen Knoten im Kopf wie ich das Array dann 
wieder an den PC zurücksenden kann.
Ich habe zum testen einfach mal jedes Feld des Arrays mit uart_getc() 
ausgegeben, jedoch kommt da alles andere an als das was ich gesendet 
habe.

Achso, die Variable flag ist
1
volatile uint8_t flag = 0;

Würde mich um weitere Tipps sehr freuen.

von Karl H. (kbuchegg)


Lesenswert?

Das ist aber ziemlich umständlich zu benutzen :-)

Warum lässt du nicht einfach uart_gets im Prinzip so wie sie ist, und 
jubelst ihr einen Ersatz für uart_getc unter, der tatsächlich auf das 
Eintreffen eines Zeichens wartet? Ist doch mit der uart_getc aus der Lib 
kein Problem sowas zu machen.
1
uint8_t uart_getc_wait()
2
{
3
  unsigned int c;
4
5
  do {
6
    c = uart_getc();
7
  } while( c == UART_NO_DATA );
8
9
  return (uint8_t)c;
10
}
11
12
void uart_gets( char* Buffer, uint8_t MaxLen )
13
{
14
  uint8_t NextChar;
15
  uint8_t StringLen = 0;
16
17
  NextChar = uart_getc_wait();         // Warte auf und empfange das nächste Zeichen
18
19
                                  // Sammle solange Zeichen, bis:
20
                                  // * entweder das String Ende Zeichen kam
21
                                  // * oder das aufnehmende Array voll ist
22
  while( NextChar != '\n' && StringLen < MaxLen - 1 ) {
23
    *Buffer++ = NextChar;
24
    StringLen++;
25
    NextChar = uart_getc_wait();
26
  }
27
28
                                  // Noch ein '\0' anhängen um einen Standard
29
                                  // C-String daraus zu machen
30
  *Buffer = '\0';
31
}

> Jedoch habe ich das gefühl, das der string nicht richtig in das
> Array LINE wandert.

Das ist richtig. So wie uart_gets momentan geschrieben ist, ist 
uart_gets darauf angewiesen, dass es die Kontrolle behält, während sie 
einen String empfängt. WEnn du das so umändern möchtest, dass das 
Empfangen eines Strings komplett im Hintergrund ohne aktives Warten 
erfolgt, dann geht das so nicht. Die Funktion uart_gets müsste dazu 
wissen, wo das nächste Zeichen im Buffer abzulegen ist. Und das wird ihr 
in der Argumentliste nun mal nicht mit übergeben.

von Maik (Gast)


Lesenswert?

Vielen Dank Karl heinz, jetzt funktioniert es.
Ich denke die völlig autake lösung werde ich ein anderes mal anstreben.

Vielleicht noch für die Nachwelt.
Wenn interesse besteht könnte man diese Variante auch ins AVR-GCC Tut 
übernehmen.

von Sven B. (mainframeosx)


Lesenswert?

Kann hier mal jemand einen Kompletten simple source erstellen, ich 
werkle seit Tagen rum und bekomme alles (HIN) aber nicht das was ich 
will. Ich stehe hier gerade voll auf dem schlauch.

von Karl H. (kbuchegg)


Lesenswert?

Sven Schwiecker schrieb:
> Kann hier mal jemand einen Kompletten simple source erstellen, ich
> werkle seit Tagen rum und bekomme alles (HIN) aber nicht das was ich
> will. Ich stehe hier gerade voll auf dem schlauch.

Was gefällt dir denn an der Lösung von 2009, 2 Postings weiter oben, 
nicht?

von Sven B. (mainframeosx)


Lesenswert?

Sie gefällt mir ganz gut, jedoch wo setzte ich uart_getc_wait und 
uart_gets im source code nun ein?

von Karl H. (kbuchegg)


Lesenswert?

1
char inLine[40];
2
3
int main()
4
{
5
....
6
7
  while( 1 ) {
8
    uart_gets( inLine, sizeof( inLine ) );
9
10
    uart_puts( "Hi. Ich hab verstanden: \"" );
11
    uart_puts( inLine );
12
    uart_puts( "\"\r\n" );
13
  }
14
}

von Sven B. (mainframeosx)


Lesenswert?

Ich habe das Problem gelöst, mein Client hatte kein Zeilenende also \r 
oder \n gesendet, da wartet der Controller ewig.

: Bearbeitet durch User
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.