Forum: Mikrocontroller und Digitale Elektronik MSP430 + uart


von Alex K. (leva87x)


Lesenswert?

Hallo zusammen,
ich versuche mit dem MSP430F5438A ein GPS Sensor über UART auszulesen 
und die empfangenen Daten in ein String zu kopieren um sie dann zu 
verarbeiten.(NMEA-Standart.. Wobei der Parser der nmealib zu umfangreich 
wäre)

Mir würde es reichen die ersten 100 Zeichen zu speichern.

mein Ansatz:

char feld[100];
int counter = 0;
int i = 0;
.....

__interrupt void USCI_A0_ISR(void) {
switch (__even_in_range(UCA0IV, 4)) {
   case 0:
   break;
   case 2:
   while (!(UCA0IFG & UCTXIFG));
   {
      feld[counter] = UCA0RXBUF;
      if(i <= 20)
         UCA1TXBUF = feld[i];
      else if(i == 20)
         i = 0;
      counter++;
      i++;
   }
   break;
}
Hier tut sich das Problem auf, dass das Feld immer überschrieben wird. 
Lösung wäre ja ein Dynamisches Array dass die Größe automatisch anpasst 
je nachdem wie viele Daten aus dem RxBuffer empfangen werden.

ich sitze schon seit Tagen an dem Problem und kann es nicht lösen.

Bitte um Hilfe

von Wechelpuffer (Gast)


Lesenswert?

Alex K. schrieb:
> ier tut sich das Problem auf, dass das Feld immer überschrieben wird.
> Lösung wäre ja ein Dynamisches Array dass die Größe automatisch anpasst
> je nachdem wie viele Daten aus dem RxBuffer empfangen werden.

Problem erkannt -> gut. Lösungsansatz -> weniger gut.

Die Daten werden in Rahmen gesendet. Jeder Rahmen hat einen eindeutigen 
Anfang und ein eindeutiges Ende. Ein möglicher Ansatz wäre, auf das 
Startzeichen zu triggern, bis zum Endzeichen zu lesen und dabei die 
maximale Puffergröße zu überwachen. Dann wird auf einen zweiten Puffer 
umgeschaltet und die nächsten Daten fließen in den zweiten Puffer, 
während der erste verarbeitet wird.

http://de.wikipedia.org/wiki/Doppelpufferung#Wechselpuffertechnik

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Alex K. schrieb:
> if(i <= 20)
>          UCA1TXBUF = feld[i];

Was soll das? Warum sendest Du im Empfangsinterrupt Daten? Und warum 
nur die ersten 20 Elemente Deines Empfangspuffers?

Außerdem überschreibst Du den Speicher nach Deinem Empfangspuffer, da Du 
"counter" nur erhöhst ... und was geschieht, wenn der den Wert 100 
angenommen hat?

von Alex K. (leva87x)


Lesenswert?

Ja wie bereits geschrieben, weiss ich dass der Puffer überschrieben 
wird. Ich suche ja nach eine Lösung nur bis auf denn Doppelpuffer fällt 
mir nichts ein. Das verarbeiten des Arrays werde ich aus dem Interrupt 
auslagern. Kannst du mir nen weiteren Tipp geben?

Ich versuche dass jetzt so zu realisieren.
Jedes Zeichen was Empfangen wird, wird auf das Startsymbol überprüft und 
so lange ist das Array geschrieben bis das Endzeichen empfangen wird. In 
dem Fall ein CR. Dann wird nach dem nächsten Startzeichen gelauscht und 
dieser wird im zweiten array gespeichert. In der main-methode wird das 
erste array verarbeitet.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das erscheint mir nicht der richtige Ansatz zu sein.

Ich würde das so umsetzen: Die Empfangsroutine empfängt Bytes und steckt 
sie in einem Empfangsfifo. Mehr macht sie nicht damit.

Das Fifo besteht aus einem Puffer und zwei Zeigern (bzw. Indices), einem 
Schreib- und einem Lesezeiger.

Die Empfangsroutine füllt so lange Daten in den Puffer ein, wie noch 
Platz im Puffer ist -- ist der Puffer voll, so werden die Daten 
verworfen.
Dazu wird der Schreibzeiger erhöht (und natürlich bei Überschreiten der 
Pufferobergrenze auf 0 gesetzt).

Der Fifofüllstand lässt sich aus Schreib- und Lesezeiger berechnen.

In der Main-Routine (oder wo auch immer) werden solange Daten aus dem 
Fifo geholt und weggeworfen, bis das Startsymbol erkannt wird. Es werden 
weiterhin Daten aus dem Fifo geholt und damit derweil das Datentelegramm 
decodiert und wie auch immer ausgewertet.

Bei Decodierungsfehlern wird in den Anfangszustand gewechselt, also 
solange Daten aus dem Fifo geholt und weggeworfen, bis wieder ein 
Startsymbol erkannt wird.

von Wechelpuffer (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Die Empfangsroutine füllt so lange Daten in den Puffer ein, wie noch
> Platz im Puffer ist -- ist der Puffer voll, so werden die Daten
> verworfen.

Das scheint mir nicht der richtige Ansatz zu sein. Da du nicht weißt, ab 
welchem Zeichen im Datenrahmen du empfängst, muss dein Puffer mindestens 
2 vollständige Datenrahmen fassen können. Und das für nur einen 
Datenrahmen.

Warum soll man nicht in der ISR auf das Startzeichen triggern? Warum 
soll man das Rahmenende nicht in der ISR ermitteln? Das Dekodieren wird 
wesentlich vereinfacht, da immer nur vollständige Rahmen verarbeitet 
werden. Es dürfte auch Resourcen schonender sein, da der Puffer nicht 
permanent im Hauptprogramm geparst werden muss: Start-/Endekennung. Und 
in einem zweiten Puffer wird der nächste Rahmen gespeichert, während der 
erste ausgewertet wird. Das ist wesentlich effizienter.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wechelpuffer schrieb:

> Da du nicht weißt, ab welchem Zeichen im Datenrahmen du
> empfängst, muss dein Puffer mindestens 2 vollständige
> Datenrahmen fassen können. Und das für nur einen
> Datenrahmen.

Nö. Der Empfangs-FIFO selbst kann auch viel kleiner sein als ein 
Datenrahmen.

Wenn für die Decodierung auf den kompletten Datenrahmen zugegriffen 
werden können muss, muss natürlich der Datenrahmen aufgehoben werden.
Aber das erfolgt nicht in der ISR, sondern an der Stelle, wo der 
Datenrahmen decodiert werden soll.
Und da genügt exakt ein Datenrahmen.

Entscheidend ist, daß sämtliche Decodierungs- und Auswertungstätigkeit 
komplett losgelöst vom Empfangspuffer erfolgt. Der Empfangspuffer ist 
nur dafür da, von der Schnittstelle empfangene Daten solange zu puffern, 
bis die Auswertungslogik in der Hauptroutine dazu kommt, sie aus dem 
Empfangspuffer herauszuholen.

Wird beispielsweise zum Decodieren mit einem kompletten Abbild des 
Datenrahmens gearbeitet, so kann die Auswertung erst dann angestoßen 
werden, wenn das Ende-Zeichen des Datenrahmens empfangen wurde. Jetzt 
kann der Datenrahmen ausgewertet werden, und die serielle Schnittstelle 
kann derweil munter weitere Zeichen empfangen. Solange die Dauer der 
Auswertung nicht länger ist als die Anzahl der Zeichen im Empfangsfifo 
mal der Bytezeit (bei 9600 Baud ca. 1 msec), geht nichts verloren.

von Wechelpuffer (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Wird beispielsweise zum Decodieren mit einem kompletten Abbild des
> Datenrahmens gearbeitet, so kann die Auswertung erst dann angestoßen
> werden, wenn das Ende-Zeichen des Datenrahmens empfangen wurde.

Nein, sobald das Starzeichen erkannt wurde, kann es losgehen.

Rufus Τ. Firefly schrieb:
> Der Empfangs-FIFO selbst kann auch viel kleiner sein als ein
> Datenrahmen.

Rufus Τ. Firefly schrieb:
> Solange die Dauer der
> Auswertung nicht länger ist als die Anzahl der Zeichen im Empfangsfifo
> mal der Bytezeit (bei 9600 Baud ca. 1 msec), geht nichts verloren.

Genau das ist der Haken. Im Hauptprogramm muss peinlichst auf schnelles 
parsen und verarbeiten geachtet werden. Das ist nicht immer einfach. 
Displayausgaben und sonstiges machen einem das Leben schwer.

Der Baustein hat 16k RAM. Die zweimal 80 Byte für den Wechselpuffer sind 
zu verschmerzen. Dafür wird die weitere Auswertung unkritisch und 
einfach.

Jetzt gibt es zwei Modelle. Alex kann ja berichten, wenn er es gelöst 
hat.

von Alex K. (leva87x)


Lesenswert?

Mit fällt es sinnvoller auf das Startzeichen zu Triggern. Wenn ich das 
Startzeichen habe kopiere ich es in das char Array und kopiere weitere 
Zeichen solange bis ich das Endzeichen empfangen habe. In der 
main-Routine erstelle ich ein Abbild des Feldes und bearbeite es. Habe 
ich den Ansatz soweit richtig nachvollziehen können?

von Wechselpuffer (Gast)


Lesenswert?

Alex K. schrieb:
> Wenn ich das
> Startzeichen habe kopiere ich es in das char Array und kopiere weitere
> Zeichen solange bis ich das Endzeichen empfangen habe.
und überwache die Pufferlänge wegen Speicherüberlauf.

Alex K. schrieb:
> In der
> main-Routine erstelle ich ein Abbild des Feldes
nein, nach dem Endezeichen schaltest du auf den zweiten Puffer um. In 
der Main kann die Auswertung im ersten Puffer direkt nach erkennen des 
Startzeichens erfolgen.

von Alex K. (leva87x)


Lesenswert?

Es funktioniert soweit, jedoch nicht so wie ich es mir vorstelle. Wie 
kann ich dem Mikrocontroller mitteilen, dass er bei einer bestimmten 
Bedingung die restlichen Zeichen aus der Uart verwerfen bzw. ignorieren 
soll?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Indem Du bei Vorliegen dieser Bedingung weiterhin die Zeichen aus der 
UART 'rausholst, aber wegwirfst.

Das Konzept habe ich beschrieben, aber das wolltest Du Dir mit Deinem 
Doppelpuffermechanismus ja nicht weiter ansehen.

von Bernd B. (Gast)


Lesenswert?

Hallo Alex K.,

ich habe mir ebenfalls einmal ein "GPS-Gerät" gebaut, mit Display und 
allem Schnick und Schnack.

Bei mir kommen die NMEA Character im Sekundentakt (schreit also nach 
zeitlicher Triggerung, um den Anfang zu finden). Die Character wandern 
in einen zyklischen Puffer mit 256 Byte. Nach Erreichen der Obergrenze 
fängt man wieder an der untersten Speicherzelle an.

Zwei Zeiger wandern durch den Puffer - ein Lese- und ein Schreibzeiger. 
Sofern beide Zeiger auf die selbe Zelle verweisen liegen keine neuen 
Daten vor. Die Daten gelangen über einen Interrupt in den Puffer und 
nach dem Schreiben wird ein Flag gesetzt, dass die Zeiger nicht länger 
auf die selbe Zelle verweisen. ... usw.

Vielleicht habe ich Dir damit einen kleinen Denkanstoss gegeben.

Gruß

Bernd

von Helmut L. (helmi1)


Lesenswert?

Wechelpuffer schrieb:
> Im Hauptprogramm muss peinlichst auf schnelles
> parsen und verarbeiten geachtet werden. Das ist nicht immer einfach.
> Displayausgaben und sonstiges machen einem das Leben schwer.

Dann hast du aber in dem restlichen Programm ein paar Designfehler drin 
wenn da solche Langlaeufer exestieren.

von Alex K. (leva87x)


Lesenswert?

Ich verstehe nicht ganz was mit dem 'Puffer' gemeint ist. Der Buffer des 
Mc oder das char Feld?

von Bernd B. (Gast)


Lesenswert?

Hallo Alex,

sollte die Frage an mich gerichtet sein: Mein Puffer mit z.B. 256 Byte 
liegt irgendwo im RAM-Bereich, also 256 Speicherzellen.

... beim Bildschirmaufbau auf meinem Display verbrauche ich übrigens 
auch mehr Zeit, als zwischen zwei Charactern mit 9.600 Baud liegt.

Gruß

Bernd

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Bernd B. schrieb:
> ... beim Bildschirmaufbau auf meinem Display verbrauche ich übrigens
> auch mehr Zeit, als zwischen zwei Charactern mit 9.600 Baud liegt.

Der von mir beschriebene Fifo-Mechanismus verwendet ja auch keinen Fifo 
von nur einem Zeichen Größe.

Ich zitier' mich mal:

> Solange die Dauer der Auswertung nicht länger ist als die
> Anzahl der Zeichen im Empfangsfifo mal der Bytezeit
> (bei 9600 Baud ca. 1 msec), geht nichts verloren.

Bei einer Fifogröße von 10 Zeichen stehen also 10 msec zur Verfügung.

von Helmut L. (helmi1)


Lesenswert?

Bernd B. schrieb:
> ... beim Bildschirmaufbau auf meinem Display verbrauche ich übrigens
> auch mehr Zeit, als zwischen zwei Charactern mit 9.600 Baud liegt.

Das ist ja auch in dem Fall nicht schlimm solange der Puffer nicht 
ueberlaeuft. Man sollte allerdings auch keine Ewigkeiten darin 
verbringen.
Der Puffer gleicht sowas ja aus.

von Wechselpuffer (Gast)


Lesenswert?

Alex K. schrieb:
> Wie
> kann ich dem Mikrocontroller mitteilen, dass er bei einer bestimmten
> Bedingung die restlichen Zeichen aus der Uart verwerfen bzw. ignorieren
> soll?

Hallo Alex,

das geht einfach mit if und einem Dummy Read. Um auf die Startbedingung 
zu triggern, liest du einfach immer auf Puffer[i] mit i = 0 und zählst 
beim Speichern der folgenden Byte den Index i hoch.

Alex K. schrieb:
> Ich verstehe nicht ganz was mit dem 'Puffer' gemeint ist.

Puffer ist der Speicher für die zwei Datenrahmen char Puffer[2][120].

von Wechselpuffer (Gast)


Lesenswert?

Helmut Lenzen schrieb:
> Dann hast du aber in dem restlichen Programm ein paar Designfehler drin
> wenn da solche Langlaeufer exestieren.

Das sehe ich anders. Das Hauptprogramm ist völlig zeitunkritisch. Der µC 
kann auch in den Low Power Mode verzweigen und sich ausschlafen. 
Zeitkritische Dinge werden per Interrupt bedient.

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.