Terfagter schrieb:> ich möchte einen String an die µC senden und dieser soll dann wieder> zurück gesendet werden. Das funktioniert soweit auch, wenn ich ein delay> von 1sec einbaue. Nehme ich das delay weg, sendet die UART den String> mehrmals (>15) und vorher und zwischendrin mehrmals ein CR.> Das zweite Problem ist, das manchmal einige Zeichen in einem String> vertaucht werden. Sende ich z.B. 123456789 kommt manchmal auch 132456798> z.B. an.
Das Problem ist, dass deine char_vorh() korrekter Weise meldet, dass
tatsächlich Character vorhanden sind. Das bedeutet aber nicht, dass eine
komplette Zeile vorliegt.
Deine uart_gets kümmert sich aber nicht weiter darum ob überhaupt
Zeichen vorhanden sind, sondern liest mittels NextChar Zeichen um
Zeichen aus, selbst wenn der Empfangsbuffer in der Zwischenzeit
kurzzeitig mal leer geworden ist. Du musst in uart_gets schon auch noch
berücksichtigen, ob überhaupt 1 Zeichen angekommen ist und getch0 daher
etwas sinnvolles zurückliefern wird.
Durch den delay gibst du der UART die Chance erst einmal genügend
Zeichen im Buffer zu sammeln, so dass sich eine schon eine komplette
Zeile im Buffer angesammelt hat, die dann von uart_gets nur noch
umkopiert zu werden braucht.
Karl heinz Buchegger schrieb:> Das Problem ist, dass deine char_vorh() korrekter Weise meldet, dass> tatsächlich Character vorhanden sind
1
intchar_vorh(void)
2
{
3
if(CharsInBuffer==0)
4
return0;
5
return1;
6
}
Wozu braucht man eigentlich eine dermaßen sinnlose Funktion?
Ok, um CharsInBuffer quasi lokal zu halten.
Die Funktion könntest du aber so abkürzen:
1
intchar_vorh(void)
2
{
3
returnCharsInBuffer;
4
}
Wobei dann die Abfrage so aussehen müsste:
1
if(char_vorh())...
Und ganz ohne diese unnötige Variable CharsInBuffer ginge das so:
1
intchar_vorh(void)
2
{
3
returnNextWriteBufferPos!=NextReadBufferPos;
4
}
Wenn die Zeiger ungleich sind ist mindestens ein Zeichen da, und es wird
eine 1 zurückgegeben...
> #define BUFFER_LEN 20
Wenn du deine Bufferlänge ein wenig besser an die binäre Rechenweise des
Zielsystems anpassen würdest, könnte die Modulo-Funktion signifikant
vereinfacht werden...
Modulo 16 ist z.B. ein Ver-Unden mit 0x0F.
Oder Pufferlänge 32 --> 0x1F
Da ist keine aufwendige Division mehr nötig.
EDIT:
Du schreibst:
> uart.h:
@ Lothar Miller:
Ich habe die Bufferlänge auf 32 gestellt und die Funktion auch, wie du
gesagt hast, umgestellt.
@ Karl heinz Buchegger:
Ich verstehe nicht ganz genau was du mit deinem zweiten Absatz meinst,
weil ich doch mit chat_vorh() überprüfe, ob Zeichen vorhanden sind und
danach die uart_gets ausführe?!? Oder sehe ich das falsch?
Ich würde es gerne umändern, aber weiß nocht nicht genau wie.
Terfagter schrieb:> @ Lothar Miller:> Ich habe die Bufferlänge auf 32 gestellt und die Funktion auch, wie du> gesagt hast, umgestellt.>> @ Karl heinz Buchegger:> Ich verstehe nicht ganz genau was du mit deinem zweiten Absatz meinst,> weil ich doch mit chat_vorh() überprüfe, ob Zeichen vorhanden sind und> danach die uart_gets ausführe?!? Oder sehe ich das falsch?
Angenommen du schickst die Zeile "Hallo World\n"
Sobald das H angekommen ist, geht deine Hauptschleife in uart_gets()
hinein (weil ja ein Zeichen da ist, nämlich das H, welches von der
Testfunktion auch korrekt gemeldet wird). Was macht uart_gets()?
1
voiduart_gets(char*Buffer,uint8_tMaxLen)
2
{
3
uint8_tNextChar;
4
uint8_tStringLen=0;
5
6
NextChar=getch0();
7
while(NextChar!='\n'&&StringLen<MaxLen-1)
8
{
9
*Buffer++=NextChar;
10
StringLen++;
11
NextChar=getch0();
12
}
13
*Buffer='\0';
14
}
es holt sich das H und speichert es in den Buffer. Und dann. Nix dann.
Die Schleife läuft solange, bis endlich der \n ankommt. Nur: Der ist
noch gar nicht angekommen! Es ist ja noch nicht einmal das 'a'
angekommen.
Das interessiert uart_gets aber nicht die Bohne! Die versucht weiter
mittels getch0() ein Zeichen nach dem anderen aus dem Buffer zu lesen,
selbst wenn gar kein Zeichen mehr im Empfangsbuffer vorhanden ist, weil
es noch gar nicht empfangen wurde. Nachdem sich getch0 dagegen nicht
wehren kann, sollte uart_gets() tunlichst beim Buffer nachfragen, ob
mindestens 1 Zeichen empfangen wurde, ehe es getch0() aufruft.
Ich habe die uart_gets mal so angepasst, wie ich es denke:
void uart_gets( char* Buffer, uint8_t MaxLen )
{
uint8_t NextChar;
uint8_t StringLen = 0;
NextChar = getch0();
while( NextChar != '\n' && StringLen < MaxLen - 1 )
{
*Buffer++ = NextChar;
StringLen++;
if(char_vorh0() == 1)
NextChar = getch0();
else
return;
}
*Buffer = '\0';
}
Jetzt wird nicht mehr zu viel übertragen, sondern es kommt fast immer
das richtige an. Aber halt nur fast.
Z.B. senke ich ooooooooooooooo und zurück kommt ein o.
Oder es sind manchmal character vertauscht.
Terfagter schrieb:> Ich habe die uart_gets mal so angepasst, wie ich es denke:>> void uart_gets( char* Buffer, uint8_t MaxLen )> {> uint8_t NextChar;> uint8_t StringLen = 0;>> NextChar = getch0();> while( NextChar != '\n' && StringLen < MaxLen - 1 )> {> *Buffer++ = NextChar;> StringLen++;> if(char_vorh0() == 1)> NextChar = getch0();> else> return;
wieso return?
uart_gets soll eine Zeile einlesen und das hat sie nicht getan!
Wenn kein Zeichen da ist, dann kann uart_gets nichts machen und muss
ganz einfach weiter warten!
Ausserdem: Wer sagt denn, dass in der Zwischenzeit nur 1 Zeichen
angekommen ist. Vielleicht sind beim Aufruf von uart_gets schon 5
Zeichen der Zeile da, zb weil die Hauptschleife ein wenig länger
gebraucht hat ehe sie dann uart_gets aufrufen konnte.
Ok jetzt hab ich verstanden, wie du das meintest.
Und das funktioniert jetzt auch soweit. Nur die Character sind manchmal
vertauscht. Und das Beispiel mit dem o, da ist der Fehler auch noch
vorhanden?!?
Terfagter schrieb:> Ok jetzt hab ich verstanden, wie du das meintest.> Und das funktioniert jetzt auch soweit.
Dann würde ich mir mal Gedanken darüber machen, wo die Dinge
durcheinander kommen, wenn ein Empfangsinterrupt an den ungünstigst
möglichen Stellen durchkommt.
zb. wird da ein ziemliches Chaos herauskommen, wenn die UART
ausgerechnet dann einen Interrupt auslöst, wenn die getch0 Funktion bei
CharsInBuffer--;
sich das CharsInBuffer schon geholt hat, drauf und drann ist ihn zu
decrementieren (jetzt kommt der Interrupt und zählt seinerseits
CharsInBuffer hoch) und den verringerten Wert nach dem Interrupt wieder
zurückschreibt. Die Erhöhung durch die ISR ist damit verloren gegangen.
Ich habe jetzt einige Zeit überlegt, aber mir fällt nichts ein, wie ich
dieses Problem gelöst kriege...
In diesem Zusammenhang gibt es aber noch ein Problem.
Ich habe einen GPS-Sender am µC der folgendes sekündlich sendet:
$GPRMC,043836.026,V,8960.0000,N,00000.0000,E,0.00,0.00,060180,,,N*70
$GPGGA,043837.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4C
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
Ich benötige aber nur ein paar Daten von $GPGGA.
Nachdem ich viel gesucht habe bin ich auf folgende Lösung gekommen:
while(1)
{
uart_gets1(buffer, 128); // Empfang der GPS-Daten. USART1
char *grad;
char *ref;
grad = strstr(buffer, "$GPGGA"); //Ab $GPGGA bis cr ausgeben
strtok( ref, "," );
strtok( NULL, "," );
grad = strtok( NULL, "," ); //Breitengrad auslesen
putstr0(grad); //Breitengrad über USART0 an PC senden
putch0(0x0D); //CR ausgeben
}
Das funktioniert auch soweit, bis auf die Tatsache, dass immer drei
Zeichen zu viel ausgegeben werden:
00000.0000
M
G
G
Ich denke, dass das mit meiner noch nicht einwandfrei funktionierenden
USART-Funktionen zusammenhängt, da das CR ja auch 3 mal mit ausgegeben
wird.
Danke für eure Hilfe.
generell musst du alle Operationen, bei denen Variablen betroffen sind,
die sowohl in einer INterrupt Funktion als auch in einer normalen
Funktion benutzt werden, dagegen absichern, dass sich die Variable
während der Operation mit dieser Variablen nicht verändern kann.
Interrupt kurzzeitig abdrehen und danach wieder zulassen.
Terfagter schrieb:>> grad = strstr(buffer, "$GPGGA"); //Ab $GPGGA bis cr ausgeben
du meinst wohl ref und nicht grad.
Code nicht abtippen sondern per Copy&Paste ins Forum kopieren!
> Ich denke, dass das mit meiner noch nicht einwandfrei funktionierenden> USART-Funktionen zusammenhängt,
Nö.
Das liegt daran, dass du nicht überprüfst, ob du es mit einem GPGGA
Datensatz zu tun hast, sondern auch bei den anderen ganz einfach das was
nach dem 2ten Komma steht rausholst.
Mit den Interrupts kurzzeitig abschalten hätte ich auch selber drauf
kommen können... Danke
Das andere verstehe ich nicht. Wenn ich grad = strstr(buffer, "$GPGGA");
ausführe erhalte ich folgenden String:
,043837.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4C
Und dann suche ich in diesem String doch die Kommata ab.
So hab ich es verstanden?!? Wo kommen dann die drei anderen Character
her?
Habs gerade nochmal ausprobiert. Wenn ich nur das in einer
while-Schleife ausführe:
char *grad;
grad = strstr(buffer, "$GPGGA");
putstr0(grad);
putch0(0x0D);
erhalte ich auch schon das Ergebnis mit jeweils drei mal "G" in dieser
Formatierung:
$GPGGA,080424.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4D
G
G
G
Terfagter schrieb:> char *grad;>> grad = strstr(buffer, "$GPGGA");> putstr0(grad);> putch0(0x0D);>> erhalte ich auch schon das Ergebnis mit jeweils drei mal "G" in dieser> Formatierung:> $GPGGA,080424.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4D>> G> G> G
Prüf wenigstens das Ergebnis vom strstr ab.
In
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
kommt nun mal kein $GPGGA vor!
Ist das so schwer zu begreifen, dass der Fall "Suchstring kommt im zu
Durchsuchenden gar nicht vor" abzutesten ist?
Dein GPS schickt dir nicht nur GPGGA Datensätze! Du kannst doch nicht
die anderen Datensätze mit dem Muster auswerten, die du auf GPGGA
anwenden willst und erwarten, dass da etwas sinnvolles rauskommt.
Terfagter schrieb:> Das andere verstehe ich nicht. Wenn ich grad = strstr(buffer, "$GPGGA");> ausführe erhalte ich folgenden String:> ,043837.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4C
Schon. Aber wie gehts weiter
1
strtok(ref,",");
2
strtok(NULL,",");
kurze Zwischenfrage:
Auf welchen String zeigt den ref?
> Und dann suche ich in diesem String doch die Kommata ab.
Das wäre die Idee.
Programmier hast du aber etwas anderes.
Terfagter schrieb:> Das andere verstehe ich nicht. Wenn ich grad = strstr(buffer, "$GPGGA");> ausführe erhalte ich folgenden String:> ,043837.026,8960.0000,N,00000.0000,E,0,0,,137.0,M,13.0,M,,*4C
Wie kommst du eigentlich drauf, dass du mit strstr diesen Teilstring
kriegst?
strstr durchsucht einen String, ob ein anderer String in ihm enthalten
ist.
Wenn ja, dann bekommt man einen Pointer zurück, der auf den Anfang des
gesuchten Teilstrings im zu durchsuchenden String zeigt. Ist der
Suchstring nicht enthalten, dann liefert strstr einen NULL Pointer
zurück.
Aber deswegen wird der Originalstring ja nicht verändert.
Karl heinz Buchegger schrieb:> Schon. Aber wie gehts weiter> strtok( ref, "," );> strtok( NULL, "," );>> kurze Zwischenfrage:> Auf welchen String zeigt den ref?>>> Und dann suche ich in diesem String doch die Kommata ab.>> Das wäre die Idee.> Programmier hast du aber etwas anderes.
Bei ref hatte ich mich ja oben verschrieben.
1 grad = strstr(buffer, "$GPGGA");
2 strtok( grad, "," );
3 ref = strtok( NULL, "," );
Ich habe das so verstanden, dass grad in der zweiten Zeile auf die
Adresse zeigt, an der sich "$GPGGA" befindet. Dann sucht strtok nach dem
ersten Komma und ersetzt es durch einen NULL-Pointer. In der dritten
Zeile wird noch dem zweiten Komma gesucht, ab dem NULL-Pointer und ein
neuer NULL-Pointer gesetzt. Zurückgegeben wird dann ein Pointer auf die
Startadresse zwischen den beiden Kommata. Ist das so richtig?
Karl heinz Buchegger schrieb:> Prüf wenigstens das Ergebnis vom strstr ab.>> In> $GPGSA,A,1,,,,,,,,,,,,,,,*1E>> kommt nun mal kein $GPGGA vor!>> Ist das so schwer zu begreifen, dass der Fall "Suchstring kommt im zu> Durchsuchenden gar nicht vor" abzutesten ist?>> Dein GPS schickt dir nicht nur GPGGA Datensätze! Du kannst doch nicht> die anderen Datensätze mit dem Muster auswerten, die du auf GPGGA> anwenden willst und erwarten, dass da etwas sinnvolles rauskommt.
Dann hatte ich das wohl nicht ganz richtig verstanden.
Ich bin davon ausgegangen, dass
grad = strstr(buffer, "$GPGGA");
den ganzen String untersucht nach "$GPGGA" und dann einen String
zurückgibt von $GPGGA bis zum nächsten CR oder LF. Aber er gibt ja einen
Pointer auf die Adresse von $GPGGA zurück, wobei ich dann noch nicht
verstehe, wie ich die anderen Datensätze ausschließen kann?!?
Karl heinz Buchegger schrieb:> Terfagter schrieb:>> Ich habe jetzt einige Zeit überlegt, aber mir fällt nichts ein, wie ich>> dieses Problem gelöst kriege...>> Nicht?> char getch0(void)> {> char NextChar = Buffer0[NextReadBufferPos];> NextReadBufferPos = ( NextReadBufferPos + 1 ) % BUFFER_LEN;>> cli();> CharsInBuffer--;> sei();>> return NextChar;> }>> generell musst du alle Operationen, bei denen Variablen betroffen sind,> die sowohl in einer INterrupt Funktion als auch in einer normalen> Funktion benutzt werden, dagegen absichern, dass sich die Variable> während der Operation mit dieser Variablen nicht verändern kann.> Interrupt kurzzeitig abdrehen und danach wieder zulassen.
Das habe ich ausprobiert, aber leider bleibt der Fehler:
Sende ich awrognaeogn kommt awrognoeang an.
Oder ich sende oooooooooooo und es kommt o an.
Terfagter schrieb:> wobei ich dann noch nicht> verstehe, wie ich die anderen Datensätze ausschließen kann?!?
grad = strstr(buffer, "$GPGGA");
if(grad != NULL)
{
Hier weiter suchen ..
}
Nur wenn grad != NULL ist dann hast du eine GPGGA Zeile in der du weiter
suchen kannst und sonst nicht.