Forum: Mikrocontroller und Digitale Elektronik Abfrage ob ein String vom UART empfangen wird


von georg (Gast)


Angehängte Dateien:

Lesenswert?

Hallo

Ich will mit einem Atmega auf Zeichen von einem GSM Modem warten.
Wenn das Modem meldet das es eine SMS empfangen hat soll die SMS vom 
Speicher geholt und weiterverarbeitet werden.

Folgende Funktion zickt herum:

//Abfrage Serielle Schnittstelle 0
if((UCSR0A & (1<<RXC0)))
{
  uart0_getstring(&temp1[0],16); //Problem: Viele Zeichen werden 
Versäumt
}

Damit überprüfe ich ob das Modem etwas gesendet hat.
Die uart0_getstring versäumt jedoch fast alles was daherkommt.

Wenn ich die Routine mit Hyperterminal von Hand durchgehe (entsprechend 
langsam) funktioniert die Funktion - am GSM Modem mit 19200 Baud jedoch 
nicht.

Meine Frage: Wenn ich die "uart0_getstring" mit Interrupt statt Polling 
löse muss ich ja trotzdem irgendwie abfragen ob das Modem etwas sendet.
Wie macht man das am besten? Wieder mit
if((UCSR0A & (1<<RXC0)))  ???

von Karl H. (kbuchegg)


Lesenswert?

georg wrote:

> Meine Frage: Wenn ich die "uart0_getstring" mit Interrupt statt Polling
> löse muss ich ja trotzdem irgendwie abfragen ob das Modem etwas sendet.

Nein.
Der Interrupt ist ja die Benachrichtigung von der UART, dass ein Zeichen 
eingetroffen ist. Wenn also der Interrupt ausgelöst wird, ist 
sichergestellt, dass ein Zeichen empfangen wurde. Kein Zeichen, kein 
Interrupt.

Edit: reden wir vom selben Interrupt?

Sieh dir einfach mal die UART Library vom Peter Fleury an (googeln). Die 
verwendet interrupt gesteuerten Empfang und einen Ringbuffer, damit kein 
Zeichen verlorengeht (solange der Buffer nicht überläuft).

von georg (Gast)


Lesenswert?

Ich habe vor, dass ich die UART Library von Peter Fleury in mein Projekt 
einbinde.

Irgendwie kann ich mir momentan nicht vorstellen wie ich feststelle das 
sich etwas am UART ändert und ich dann 2 Srings einlese die aus 
folgender Antwort vom Modem stammen: \r\n+CMTI: "SM", 5\r\n

Der zweite eingelesene String (bis "\n" wird gelesen) soll anschließend 
behandelt werden.


Meine Idee: Die globale Variable "x" die größer als 0 ist, falls Zeichen 
im Empfangsbuffer sind.

Mein Hauptprogramm wird dann so aussehen:

while (1)
{
   if(x)
   {
     getstring(...);
   }
}


Wäre das eine Lösung?

von Karl H. (kbuchegg)


Lesenswert?

georg wrote:

> Wäre das eine Lösung?

So ungefähr. Sieh dir die Library an. Die kommt mit einem Demo, wie man 
sie benutzt.

von georg (Gast)


Lesenswert?

Es funktioniert mit Peters Library.
Bitte kurz anschaun ob die Lösung auch sinnvoll ist:

1
/*Zusätzliche Funktion zum checken ob das Modem mindestens 1 Byte gesendet hat
2
*/
3
int check_data_uart(void)
4
{
5
6
  if ( UART_RxHead != UART_RxTail ) {
7
        return 1;   /*data available */
8
    }
9
  else {
10
        return 0;  /*no data available */
11
    }
12
}
13
14
15
16
17
   //Hauptprogramm
18
   for(;;)
19
    {
20
  
21
  
22
  if (check_data_uart())
23
  {
24
    uart_gets(buffer,7);  //7 Zeichen werden eingelesen
25
    uart_putc('\n');
26
    uart_putc('\r');  
27
    uart_puts(buffer);   //gibt Buffer wieder aus
28
  }
29
  else
30
  {
31
    uart_putc('#');   //flutet Hyperterminal mit # Zeichen
32
  }
33
34
    }

von Stefan E. (sternst)


Lesenswert?

> Bitte kurz anschaun ob die Lösung auch sinnvoll ist:

check_data_uart prüft ja nur, ob Zeichen da sind, aber nicht, wie viele. 
Das funktioniert also nur dann sinnvoll, wenn uart_gets seinerseits ggf. 
auf die restlichen Zeichen wartet. Wenn es das aber tut, wozu dann noch 
das check_data_uart vorher?

von georg (Gast)


Lesenswert?

@Stefan

Ja, die Funktion gets() waret auf nachfolgende Zeichen.


Meine Anforderung ist:
GSM-Modem sendet zu irgendeinem Zeitpunkt Daten aus (einen String).
Der AVR soll nicht ewig auf einen String warten, sondern nebenbei noch 
Tastenabfragen und sonstiges machen.

Würde man nicht check_data_uart() abragen, dann würde der AVR ewig in 
gets() warten.

Irgendwie gefällt mir meine Funktion nicht so ganz, da shon bei einem 
einzigen Zeichen ein String eingelesen wird und der AVR somit hängen 
bleibt, weil kein '\n' daherkommt.

Hat dazu jemand eine Idee?

(Falls nicht, bin ich gezwungen einen Watchdog einzubauen)

mfg

von Karl H. (kbuchegg)


Lesenswert?

georg wrote:

> (Falls nicht, bin ich gezwungen einen Watchdog einzubauen)

Quatsch

Dann kannst du die String-Einlese-Funktionalität so nicht in eine 
Funktion stecken, sondern in die Hauptschleife. Du liest nicht einen 
String ein, sondern holst ein Zeichen von der UART, wenn eines da ist. 
Dieses Zeichen wird an die bereits geholten Zeichen hinten drangehängt. 
Wenn sich dadurch eine komplette Eingabezeile ergibt -> super, den jetzt 
kompletten String verarbeiten. Wenn nicht, gehts weiter in der 
Hauptschleife.

1
char receivedString[80];
2
uint8_t receivedStringLen;
3
....
4
5
int main()
6
{
7
  int c;
8
9
   ....
10
11
12
  receivedStringLen = 0;
13
  receivedString[0] = '\0';
14
15
  while( 1 ) {
16
17
    c = uart_getc();
18
    if ( !( c & UART_NO_DATA ) ) {    // die UART hat was empfangen
19
      if( c == '\n' ) {
20
        // mach was mit dem jetzt kompletten String
21
        ....
22
        receivedString[0] = '\0';
23
        receivedStringLen = 0;
24
      }
25
26
      else {
27
        receivedString[ receivedStringLen++ ] = c;
28
        receivedString[ receivedStringLen ] = '\0';
29
      }
30
    }
31
32
    ....
33
  }
34
}


Du musst anfangen, auf einem µC deine Aufgaben in kleine Häppchen 
aufzuteilen. Die Hauptschleife in main() ist dann dafür zuständig, dass 
reihum immer wieder diese kleinen Häppchen ausgeführt werden. So ergibt 
sich ganz von alleine, dass dein µC scheinbar mehrere Dinge gleichzeitig 
macht.

PS: Fehlerabfragen musst du noch einbauen.
uart_getc liefert nicht nur UART_NO_DATA, sondern auch Fehlercodes. Und 
bei dem dranhängen ans Array wäre es gut auf einen möglichen 
ArrayOverflow zu testen.

von Stefan E. (sternst)


Lesenswert?

Karl heinz Buchegger wrote:

> Dann kannst du die String-Einlese-Funktionalität so nicht in eine
> Funktion stecken, sondern in die Hauptschleife.

Na ja, so pauschal kann man das nicht unbedingt sagen.
Ich würde es z.B. so machen, dass eine Funktion die Zeichen (wenn denn 
eines da ist) in einem lokalen static Array sammelt und dann über den 
Rückgabewert mitteilt, ob der String komplett ist. Z.B. ein Zeiger auf 
das lokale Array bei "String da" und sonst 0. In der Hauptschleife 
könnte dann sowas stehen:
1
char * str;
2
...
3
if (( str = GetString() )) {
4
    // tue was mit dem String
5
}
Damit wäre dann das komplette Einlesen (inkl. aller Checks) in eine 
Funktion ausgelagert, und die Hauptschleife wäre deutlich 
übersichtlicher.

von Peter D. (peda)


Lesenswert?

Hier ein einfaches Beispiel mit String-Empfang und Auswertung:

Beitrag "programmierbare 16 Kanal PWM Lightshow"

Die UART ist in Software, aber da das Programm modular ist, kann man das 
leicht ändern.
Das Hauptprogramm läuft nebenher unbeeinflußt weiter.


Peter

von georg (Gast)


Lesenswert?

vorübergehend mal danke an alle. ich werde mich den Antworten mal näher 
widmen

von georg (Gast)


Lesenswert?

Hallo

Das Einlesen eines Strings (mit Endezeichen '\n') funktioniert nicht 
richtig. Kann mir jemand weiterhelfen
1
#define size_of_buffer 10
2
3
4
int check_string_availiable(char *combuffer, int buffersize)
5
{  
6
  uint8_t i = 0;
7
  uint8_t c;
8
9
  
10
  if (check_data_uart()) //wenn >=1 Byte empfangen wurde
11
  {
12
       c = uart_getc();
13
    
14
    switch( c )
15
    { 
16
      case '\n':    //wenn Stringende erreicht
17
        i = 0;
18
        return 1;
19
      break;
20
      
21
      default:    //wenn Stringende nicht errreicht
22
        if( i < buffersize)
23
        *combuffer++ = c;
24
    }
25
  }
26
  
27
  return 0;
28
29
}
30
31
32
int main(void)
33
{
34
    char buffer[size_of_buffer];  //Empfangsbuffer
35
36
    for(;;)
37
    {
38
 
39
    if ( check_string_availiable(&buffer[0], size_of_buffer) )
40
    {  
41
      uart_puts(&buffer[0]); //Eingelesenen String ausgeben
42
      uart_putc('\r');
43
      uart_putc('\n');
44
    }
45
  
46
  }
47
}

von georg (Gast)


Lesenswert?

Wenn ich folgendes an den AVR Sende:

abcdefgh '\n'

kommt folgendes zurück

h4 '\r''\n'


Warum gibt er nur den letzten Buchstaben aus und woher kommt die Zahl 4 
???

von Stefan E. (sternst)


Lesenswert?

Sorry, wenn das jetzt etwas hart rüberkommt, aber die ganze Funktion 
check_string_availiable ist "für'n Hintern".

1)
Wozu das check_data_uart()? Genauso gut kannst du die Rückgabe von 
uart_getc auswerten. Solltest du wegen der Fehlerauswertung ja eh 
machen.

2)
Du schreibst das empfangene Zeichen immer wieder an die selbe Stelle.
(Ursache für das "Warum gibt er nur den letzten Buchstaben aus")

3)
Nirgendwo machst du anstallten, den String zu terminieren.
(Ursache für das "und woher kommt die Zahl 4")

von Peter D. (peda)


Lesenswert?

Du hast scheints flüchtig auf mein get_command( void ) geschaut, aber 
wohl arge Probleme mit der Sprache C:

Man muß nicht auf Biegen und Brechen Parameter übergeben, wenn es eh nur 
einen möglichen Aufruf gibt.

Parameter werden als Kopie übergeben, d.h. Änderungen (Pointerincrement) 
sind wirkungslos. Der Compiler wird es wohl auch wegoptimiert haben.

Das "static u8 idx" hat seinen Grund in dieser Funktion. Schau in Dein 
C-Buch, was static lokale Variablen sind.


Peter

von georg (Gast)


Lesenswert?

Bin auf die Fehler jetzt eingegangen und das Programm macht was ich 
will.
Ließt einen String ein String bis '\n' aus dem Empfangspuffer ohne das 
der AVR in einer while Schleife wie bei üblichem einlesen mit "normalem" 
getstring()

Bitte um Verbesserungsvorschläge im Algorithmus:
1
uint8_t i = 0; //global definiert
2
3
int check_string_availiable(char *combuffer, int buffersize)
4
{  
5
  unsigned int c;
6
7
    c = uart_getc();
8
    
9
    if (!(c & UART_NO_DATA)) //wenn >=1 Byte empfangen wurde
10
    {
11
    
12
    switch( c )
13
    { 
14
      case '\n':    //wenn Stringende erreicht
15
        *(combuffer+i) = 0; //String 0 terminiert
16
        i = 0;
17
        return 1;
18
      break;
19
      
20
      default:  //wenn Stringende nicht erreicht
21
        if(i < (buffersize-1)) 
22
                             // 0 bis 8, Platz 9 ist für Zahl 0 reserviert
23
        {
24
         *(combuffer+i) = c;
25
         i++;
26
        }
27
        else
28
        {
29
         i = 0; //wieder von vorne beschreiben
30
        }
31
32
    }
33
34
  }


Eine weitere Frage habe ich. Es ist durchaus möglich dass das GSM Modem 
auf die schnelle 200 Zeichen ausspuckt in dem 4 Strings enthalten sind.
Für ein 160 Zeichen SMS brauche ich ein 161 Zeichen Array in dem ich das 
SMS + '\0' abspeicher.

Wie groß sollte man den Empfangspuffer vom UART wählen? 200 Zeichen?
Wenn ich dass mit dem Ringpuffer richtig verstanden habe, kann dieser 
auch viel kleiner sein als 200 nur wird dann der Abarbeitungsvorgang 
gebremst? Bitte korrigiert meine Aussage...

mfg

von georg (Gast)


Lesenswert?

@Peter

Danke für den Hinweis, habe gegoogelt und weiß jetzt warum du mit static 
und nicht mit irendeiner globalen Variable arbeitest.
Habe ich ausgebessert...

von georg (Gast)


Lesenswert?

Hat keiner eine Idee?

von Karl H. (kbuchegg)


Lesenswert?

> Eine weitere Frage habe ich. Es ist durchaus möglich dass das
> GSM Modem auf die schnelle 200 Zeichen ausspuckt in dem 4
> Strings enthalten sind.
> Für ein 160 Zeichen SMS brauche ich ein 161 Zeichen Array in
> dem ich das SMS + '\0' abspeicher.
>
> Wie groß sollte man den Empfangspuffer vom UART wählen?

So gross, dass er alle Zeichen aufnehmen kann, die reinkommen, während 
du einen String weiterverarbeitest.

Hinweis: Im Vergleich zu der Rechengeschwindigkeit ist eine UART 
Übertragung bei 9600 oder 19200 Baud schnarchlangsam.

> Wenn ich dass mit dem Ringpuffer richtig verstanden habe,
> kann dieser auch viel kleiner sein als 200 nur wird dann der
> Abarbeitungsvorgang gebremst? Bitte korrigiert meine Aussage...

Du hast nicht verstanden was dieser Buffer macht.
Der ist sozusagen dein Eingangslager aus dem sich die Arbeiter ihre 
Materialien holen, während die Lieferanten neue Ware bringen. Wenn die 
Arbeiter wesentlich schneller Material aus dem Lager holen, als die 
Lieferanten nachliefern, genügt ein kleines Lager. Nur dann, wenn die 
Lieferanten ständig nachliefern und keiner holt das Zeugs ab, kriegst du 
ein Problem. Egal wie gross du das Lager machst, es wird immer 
irgendwann voll sein.

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger wrote:

> Aber diesen check.... Mist braucht kein Mensch.

Warum soll etwas Mist sein, nur weil Du es nicht verwendest (nicht 
verstehst)?

Ich mache es ja ähnlich und finde es äußerst praktisch. Das Main muß 
dann nämlich nirgends warten und kann andere Sachen machen.

Die Routine holt sich die Bytes vom UART-FIFO und prüft auf \n oder \r. 
Und erst dann übergibt sie den Puffer dem Parser zur Auswertung.

Bei mir macht sie noch einiges zusätzlich (Echo, Backspace, Umwandlung 
in Großbuchstaben).


Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger wrote:
> Karl heinz Buchegger wrote:
>
>> Aber diesen check.... Mist braucht kein Mensch.
>
> Warum soll etwas Mist sein,

Weil ich beim hochscrollen, bei der falschen Version gelandet bin. Die 
von 18:08

int check_string_availiable(char *combuffer, int buffersize)
{
  uint8_t i = 0;
  uint8_t c;


  if (check_data_uart()) //wenn >=1 Byte empfangen wurde
  {
       c = uart_getc();


Und das ist Mist

von Karl H. (kbuchegg)


Lesenswert?

> nur weil Du es nicht verwendest (nicht
> verstehst)?

Das nehm ich jetzt persönlich.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

georg wrote:

> Das Einlesen eines Strings (mit Endezeichen '\n') funktioniert nicht
> richtig.

Was funktioniert bzw. was funktioniert GENAU nicht?

Auf Fehlermöglichkeiten in check_data_uart() und uart_getc() gehe ich 
nicht ein, weil die Funktionen in deinem Codefetzen fehlen.

> int main(void)
> {
>     char buffer[size_of_buffer];  //Empfangsbuffer

buffer ist hier eine lokale Variable und deshalb nicht 
notwendigerweise mit Nullbytes initialisiert!

Und wird hier

>       case '\n':    //wenn Stringende erreicht
>         i = 0;
>         return 1;
>       break;
>
>       default:    //wenn Stringende nicht errreicht
>         if( i < buffersize)
>         *combuffer++ = c;

auch nicht bewusst mit einem Nullbyte abgeschlossen. Auch nicht 
unbewusst, wenn per UART nur ASCII Zeichen und keine Binärzeichen 
("Zufallsnull") kommen.

Dadurch kann dies

>       uart_puts(&buffer[0]); //Eingelesenen String ausgeben

einen Pufferüberlauf beim Lesen produzieren. uart_puts gibt solange aus, 
bis zufällig ein Nullbyte gefunden wird. Es wird eine Ausgabe 
produziert, die den gesendeten String liefert, aber manchmal auch Müll 
hinterher.

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.