Forum: Compiler & IDEs Datensatz aus UART "gewinnen"


von Sven (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

seit fast genau einen Monat sitze ich, jeden abend, an folgenden 
Problem: Ich möchte Datensätze von GPS-Empfänger über die 
Interruptroutine von UART1 Einlesen. Die gwünschten Daten Parsen und mit 
anderen Daten vom uC über den UART0 schreiben (Loggen).
Mein Ansatz ist folgender: Bei jedem empfangenden Zeichen vom UART in 
die entsprechende ISR springen, wenn Zeichen $ ist ein Writeflag, und 
den Buffercounter auf 0 setzen und solange ins Characterarray schreiben 
bis Zeichen /r ist, dann das Buffer Write Flag deaktivieren und ein 
weiteres Flag setzten um den String im Mainloop zu parsen. Dann die 
Flags zurücksetzten und das ganze beginnt von vorn. Und das funktioniert 
nicht, ich habe in den vergangenen Wochen schon einiges ausprobiert aber 
diese scheinbar triviele Sache klappt nicht, ich bekommen keinen 
korrekten Datensatz "zusamnmen", auf dem Terminalprogramm werden 
manchmal ein kompletter Datensatz ausgegeben, und oft nur ein Teil 
davon. Und manchmal "Sturzt" der uC ab.
Der GPS-Empfänger läuft mit 4800 Baud, die Ausgabe über den zweiten UART 
mit 115200 Baud. Ich arbeite mit WinAVR und einem ATmega 1284p. 
Vielleicht kann ich einen Tip bekommen, wo mein Denkfehler liegt.

von Peter II (Gast)


Lesenswert?

Ich kann eigentlich kein Fehler im code sehen. (bis auf das C Dateien 
auch die Endung .c haben sollten).

Hast du einen Quarz oder nutzt du den internen Oszilator?

von Sven (Gast)


Angehängte Dateien:

Lesenswert?

Sorry,

habe das c vergessen, wurde berichtigt. Ich habe alles logische und 
unlogische Ausprobiert, sitze ja auch schon seit Wochen daran. AUf 
meinem Terminalprogramm erscheint nur folgendes:
,M,,0000*47
0,177.80,030114,,,A*7C
1.2,117.2,M,35.6,M,,0000*43
,030114,,,A*79
,030114,,,A*75
552,N,01534.8422,W,1,05,1.2,117.3,M,35.6,M,,0000*43
N,0.0,K,A*04
,M,,0000*4B
,030114,,,A*76
$M,,0000*48
N,0.4,K,A*03
0,M,35.6,M,,0000*48
,,A*7C
01534.8422,W,0.04,177.80,030114,,,A*7D
534.8422,W,1,06,1.1,116.8,M,35.6,M,,0000*4D
,030114,,,A*7D
A*04
Also leider nichts brauchbares. Ich verwende einen einen Quarz (8 Mhz) 
und benutze den UART immer zum "debuggen" seit jeher ohne Probleme. Ich 
habe auch schon mit dem Logikanalyzer nachgeshen, ohne Auffälligkeiten. 
Die Interruptroutine wird beim Ausgeben des Strings zwar angesprungen, 
aber aufgrund der Flags werden dem String keine Zeichen hinzugefügt wenn 
er im "Mainloop" geparst wird. Das während dessen Zeichen verloren gehen 
ist klar, aber in meinem Fall nicht wichtig. Auch das Abschlaten des 
Interrupts während der Abarbeitung im "Mainloop" bringt keine positiven 
Ergebnisse. Leider.

von Peter II (Gast)


Lesenswert?

dann zeige uns doch mal alles. Wo ist SER_OUT?

von Sven (Gast)


Lesenswert?

Der SER_OUT ist Standard und schreibt ein Byte zum UART nachdem er 
gewartet hat bis das Byte zum Senden bereit ist:

void SER_OUT(uint8_t byt)
{
while ( ! (UCSR0A & (1<<UDRE0)));
UDR0 = byt;
}

Ich hatte auch schon die Vermutung das es irgentein Timing-Problem ist, 
oder das etwas im Speicher überschrieben wird.

von Peter II (Gast)


Lesenswert?

Sven schrieb:
> Ich hatte auch schon die Vermutung das es irgentein Timing-Problem ist,
> oder das etwas im Speicher überschrieben wird.

was hast du denn noch sie im Programm was du uns nicht zeigst? 99byte 
für den Puffer sind ja schon recht viel für den atmel. Was zeigt die 
Speicherauslastung an?

von Sven (Gast)


Lesenswert?

Nö, 99 Bytes sind doch nicht viel für einen ATmega 1284p. Ein NMEA 
Datensatz hat 80 druckbare Zeichen gefolgt von einen \r\n und ein \0 den 
ich Anhänge. Die 99 habe ich gewählt weil das auf jeden Fall passt. Und 
eben diese 80 druckbaren Zeichen will ich ja haben.

von Peter II (Gast)


Lesenswert?

du kannst mal folgendes Versuchen:

statt:

nmea[gps_buf_cnt] = gps_buf_rxc;

mal folgendendes

nmea[gps_buf_cnt] = gps_buf_cnt + '0'


und dann mal bitte die ausgabe anschauen.

von Sven (Gast)


Lesenswert?

Nein das geht auch nicht. Bei meinem Projekt handelt es sich um eine 
gyrosskopische Kamera für's Motorrad, ein Lagesensor erfasst die 
Schräglage des Motorrades und richtet eine Aktioncam entschrechend 
parallel zum Horizont aus, mittels eines Servos. Das zu programmieren 
war für mich nicht ganz so einfach, funktioniert aber bestens - trotz 
einiger mathematischer Hürden. Als "Gimik" will ich jetzt auch noch die 
GPS-Daten zum Film loggen, neben den Daten des Lagesensors um den / die 
Filter zu optimieren. Aber die scheinbar einfachen Dinge haben ihre 
Tücken.

von Peter II (Gast)


Lesenswert?

Sven schrieb:
> Nein das geht auch nicht.
was geht nicht, was kommt denn da raus?

> Bei meinem Projekt handelt es sich um eine
> gyrosskopische Kamera für's Motorrad, ein Lagesensor erfasst die
> Schräglage des Motorrades und richtet eine Aktioncam entschrechend
> parallel zum Horizont aus, mittels eines Servos. Das zu programmieren
> war für mich nicht ganz so einfach, funktioniert aber bestens - trotz
> einiger mathematischer Hürden. Als "Gimik" will ich jetzt auch noch die
> GPS-Daten zum Film loggen, neben den Daten des Lagesensors um den / die
> Filter zu optimieren. Aber die scheinbar einfachen Dinge haben ihre
> Tücken.

also ist das doch nicht der ganze Quelltext - der Fehler liegt mit 
Sicherheit woanders.

von Rainer B. (katastrophenheinz)


Lesenswert?

Narbend, Ich vermute, der Baudratenfehler bei 115200Bd @8MHz ist zu 
gross für den, der am anderen Ende der Leitung lauscht. Daher kommt dort 
nur Schrott an.

Dreh' zum Verifizieren mal die Baudrate von 115200 auf 38400 Bd runter. 
Da ist der Fehler nur noch 0.2%. Dann sollte das klappen.

Richtige Abhilfe: Baudratenquarz ( die mit der "krummen" Frequenz ) 
verwenden ( z.B. 14.7456MHz - da ist der Baudratenfehler dann 0 )

Gruss, Rainer

von fuelre (Gast)


Lesenswert?

Hast du mal ausgerechnet wie groß der Fehler der UART bei 115kBaud ist?
Denn ich habe die Geschwindigkeit mit keinem ATMEGA stabil zum laufen 
bekommen!

von fuelre (Gast)


Lesenswert?

Ein bisschen zu langsam mit dem tippen auf dem Tablet

von Henry (Gast)


Lesenswert?

Also mein ATmega1284P (@8 oder 20 MHz) komuniziert schon seit jeher nur 
mit 115200Baud, ohne Probleme.
Da horcht aber auch ein PC...

von Peter II (Gast)


Lesenswert?

das eine Falsche Baudrate für das Ergebnis verantwortlich ist, kann ich 
mir nicht vorstellen, da müssen viel mehr ungültige Zeichen vorhanden 
sein. Für mich sieht das aus, also ob jemand anderes noch an den 
gleichen Variablen Änderungen vornimmt. (Speicher überschreiber)

von Uwe (de0508)


Lesenswert?

Hallo Sven,

ich habe dein Programm überflogen und würde es komplett anders machen.

Erstmal einen Sende- und Empfangsfifo mit z.B. 64Byte Puffergröße im 
Interruptbetrieb definieren. Siehe Peter Dannegger im Forum.

Lesen kann man so einen Fifo nur als First-in-First-out. Danach kommt 
noch eine Parser, der den Datenstrom analysiert und danach werden aus 
den "Token" die entsprechenden Aktionen/ Befehle abgeleitet.

Ich nehme mal an es ist ein NMEA Datenformat, das dieser Erläuterung 
genügt.

http://www.kowoma.de/gps/zusatzerklaerungen/NMEA.htm
http://de.wikipedia.org/wiki/NMEA_0183

: Bearbeitet durch User
von Jonas B. (jibi)


Lesenswert?

>das eine Falsche Baudrate für das Ergebnis verantwortlich ist, kann ich
>mir nicht vorstellen, da müssen viel mehr ungültige Zeichen vorhanden
>sein. Für mich sieht das aus, also ob jemand anderes noch an den
>gleichen Variablen Änderungen vornimmt. (Speicher überschreiber)

Nicht falsch, sondern mit zu großer Abweichung. Die driftet dann auf PC 
Seite irgendwann weg...Würde sehr gut die "Aussetzer" erklären.

Gruß Jonas

: Bearbeitet durch User
von Jonas B. (jibi)


Lesenswert?

Läst du dir im terminalprogramm zur Kontrolle auch immer die roh-Daten 
anzeigen???

von Peter II (Gast)


Lesenswert?

Jonas Biensack schrieb:
> Nicht falsch, sondern mit zu großer Abweichung. Die driftet dann auf PC
> Seite irgendwann weg...Würde sehr gut die "Aussetzer" erklären.

nein, wenn es ein Baudratenfehler ist, dann werden einzelne Bits nicht 
korrekt ausgewertet und man bekommt viele Steuerzeichen zu sehen.

Uwe S. schrieb:
> Hallo Sven,
>
> ich habe dein Programm überflogen und würde es komplett anders machen.
>
> Erstmal einen Sende- und Empfangsfifo mit z.B. 64Byte Puffergröße im
> Interruptbetrieb definieren. Siehe Peter Dannegger im Forum.

wo soll da der Vorteil sein? Er braucht damit auch noch mehr rum. Finde 
es gar nicht so schlecht umgesetzt.

von Peter D. (peda)


Lesenswert?

Peter II schrieb:
> wo soll da der Vorteil sein?

Der Vorteil ist, daß die Stringverarbeitungen sich nicht gegenseitig an 
beliebiger Stelle unterbrechen können bzw. sich behindern (dead-lock).

Erfolgen alle Verarbeitungen im Main, ist die Reihenfolge immer klar 
definiert.

Der Interrupt macht nur die reine Pufferung, dann kann nichts falsch 
laufen.
Und nur das getchar/putchar vom Main zur FIFO und zurück muß atomar 
sein.

von Peter II (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Der Interrupt macht nur die reine Pufferung, dann kann nichts falsch
> laufen.

und genau das macht er doch.

Er puffer in der ISR einen Nema-Datensatz und gibt ihn erst frei wenn 
das abschlusszeichen erkannt wurden ist.

von Martin M. (capiman)


Lesenswert?

@Sven
Kannst du (in main) mal statt dem ganzen Datensatz nur immer jeweils
die ersten x Bytes, z.B. 4 Bytes ausgeben?
Wenn alles richtig läuft (Sync auf NMEA-Daten),
dann solltest du immer als 1. Zeichen ein '$' sehen.

Was/welches Programm benutzt du zum Anzeigen auf der PC-Seite?
Benutzt du einen USB <-> Seriall Converter? Welchen?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Peter II schrieb:
> und genau das macht er doch.

Nö.
Er macht die komplette Verarbeitung darin. Und dann wartet er erstmal 
gemütlich, bis alles weiter gesendet wurde.
Und wenn dabei schon weitere Bytes reinkommen, kracht es eben.

Soweit genug RAM vorhanden ist, sollte der FIFO mindestens 2 komplette 
Pakete puffern können, d.h. während der Verarbeitung darf ruhig schon 
das nächste reinkommen.

von Peter II (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Und wenn dabei schon weitere Bytes reinkommen, kracht es eben.

Hast du den code überhaupt mal angeschaut?

So lange der aktuelle Datensatz nicht verarbeitet ist, macht die ISR 
nichts

 if(gps_buf_rdy == 0)

von Martin M. (capiman)


Lesenswert?

@Peter
Bist du dir sicher, dass es kracht?
Wenn ein Paket komplett empfangen ist, dann wird gps_buf_rdy
gesetzt. Ab dem Zeitpunkt holt die ISR die Zeichen und
verwirft sie.
Selbst wenn die Routine zum Ausgeben mitten im NMEA Paket fertig
wird, werden alle Zeichen außer '$' verworfen,
weil gps_buf_wrf noch nicht gesetzt ist,
und gps_buf_wrf wird erst gesetzt, wenn ein '$' empfangen worden ist.

Es kann natürlich vorkommen, dass ganze NMEA Pakete verloren gehen,
aber kaputt sollten sie eigentlich nicht sein...

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Wenn das der wirkliche Code ist, müßte doch jede Zeile im 
Terminalprogramm mit '$' beginnen.

von Peter II (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Wenn das der wirkliche Code ist, müßte doch jede Zeile im
> Terminalprogramm mit '$' beginnen.

genau zu dem Schluss bin auch gekommen, aus dem Grund die Vermutung das 
in den nicht Sichtbaren teil vom Code ein Speicherüberschreiber 
stattfindet.

von Sven (Gast)


Lesenswert?

Vieln Dank für die Antworten. Also ...

* Ja das ist das ganze Programm und nicht nur ein Tail davon, ich habe 
nur die Initialisierung der UARTS weg gelassen, weil weil klar.

* Die Baudraten habe ich mittels Datenblatt definiert, also UART0 
(Ausgabe) mit 115.200 Baud, U2X0 = 1, UBBR0 8 => -3,5 % Fehler. Und 
UART1 (Eingabe) mit 4.800 Baud, U2X1 = 0, UBBR1 103 => 0.2 % Fehler.

* Ich "debugge" schon immer so, und hatte noch nie Probleme damit, auch 
mit 8 MHz. Als Terminalprogramm benutze ich Hyperterm. HTerm zeigt zeigt 
auch nichts anderes an.

* Ich benutze 8 MHz, weil und 3.3 Volt weil damit der mC Frequenz und 
Spannungsmässig ich grünen bereicht liegt, wie im Datenblatt angegeben.

* Richtig es gehen Zeichen verloren, ist in diesem Fall aber nicht 
wichtig das immer neue Daten nachkommen.

* Ich habe keinen Ringpuffer verwendet weil ich den "Aufwand" für 
grösser halte und jeben NICHT jedes Zeichen mitbekommen muss. Wenn 
mehrere Datensätze übersprungen werden ist es egal. Siehe Punkt oben.

* Als Pegelwandler benutze ich einen MAX3233, mit der im Datenblatt 
angegebenen Standardbeschaltung - Funktioniert seit Jahren ohne 
Probleme.

* Die "Logik" der Flags stimmen, die habe ich mittels Logicanalyser 
visualisieren lassen.

* Der UART0 hängt (über den MAS3232) direkt am COM1, welcher sich auf 
dem Mainboard, des PC's befindet.

* Wenn ich mir noch in der ISR Routine das erste Zeichen ausgeben lasse 
ist ein ein '$' und die nach Erkennung des '/r' stimmt auch die länge 
des Datensatzes.

* Der Compiler zeigt keine Warnings oder Errors an.

* Ich glaube nicht das die ISR zu lang ist, es sind ja nur ein paar If 
abfragen, ein Zähler und ein paar Flags. Ich kann mir nicht vorstellen 
das dies bei 8 MHz zu viel ist. Die Visualisierung am Logicanalyser 
zeigt auch keine überschneidungen an - im Gegenteil da ist noch massig 
Zeit.

Meine Denke ist folgende, sobald ein '\r' erkannt wird das Buffer Write 
Flag gelöscht und eben nichts mehr in dem Buffer geschrieben. Durch 
setzen des Buffer Ready Flags signalisiere ich der Mainloop das der 
Buffer nun "verarbeitet" (in meinem Fall einfach über den anderen UART 
ausgegeben) werden soll. Wenn das geschehen werden die Flags 
entsprechend gesetzt, und es wird wieder auf ein '$' gewartet und der 
Buffer neu beschrieben.

Das einzige was ich mir vorstellen kann (oder eben nicht) das der Buffer 
auf irgendeinem Grund überschrieben wird, wenn ja warum. Da bin ich mit 
meinen "Latin" an Ende. Ich werde heute Abend mal den Controller 
wechseln, weil mir sonst nichts mehr einfällt.

Vielleicht hat ja noch jemand einen Hinweis, vielen Dank für die 
Antworten bis jetzt.

von Peter II (Gast)


Lesenswert?

Sven schrieb:
> Ja das ist das ganze Programm und nicht nur ein Tail davon, ich habe
> nur die Initialisierung der UARTS weg gelassen, weil weil klar.

nein ist es nicht. die SER_OUT fehlte auch.

Also stell uns bitte das komplette Programm zur Verfügung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven schrieb:
> Vieln Dank für die Antworten. Also ...
>
> * Ja das ist das ganze Programm und nicht nur ein Tail davon, ich habe
> nur die Initialisierung der UARTS weg gelassen, weil weil klar.

Nein, überhaupt nicht.

Dein Programm scheitert doch schon daran, das "HALLO WELT" am Anfang 
auszugeben, siehe Beitrag "Re: Datensatz aus UART "gewinnen""

von Martin M. (capiman)


Lesenswert?

Du konntest dir auch mal 2 nmea buffer anlegen,
in beide das gleiche reinschreiben.
Dann kannst du in main vergleichen,
ob die beiden irgendwo unterschiedlich sind.

von Stefan E. (sternst)


Lesenswert?

Peter II schrieb:
> Also stell uns bitte das komplette Programm zur Verfügung.

Und auch gleich den Build-Output und die lss-Datei.

von Sven (Gast)


Lesenswert?

Vielen Dank für die Antworten, waren eine echte Hilfe für mich. Bin ein 
ganzes Stück weiter gekommen, ich bekomme jetzt vernünftige Datensatze 
"geliefert". Ich las das Programm mal eine Nacht durchlaufen um zu sehen 
ob es funktioniert (langfristig) - also stabil ist. Im Eifer der 
Programmierung habe ich noch ein paar Zeilen eingefügt um die Prüfsumme 
zu berechnen (sind drei Zeilen in der ISR), und dort werden die Zeichen 
ja eh schon durchlaufen - das Buffer Ready Flag für die Mainloop ist 
also nur dann 1 wenn der Datensatz auch wirklich gültig ist (Prüfsumme 
XOR).

Nochmals Danke.

von Quark (Gast)


Lesenswert?

Jonas Biensack schrieb:
> Nicht falsch, sondern mit zu großer Abweichung. Die driftet dann auf PC
> Seite irgendwann weg...

Beim UART-Empfang werden mit jedem Start-Bit, i.e. für jedes einzelne 
Zeichen die Karten neu gemischt. Wie soll da irgendetwas irgendwann ins 
Driften kommen?

von Michael (Gast)


Lesenswert?

Sven schrieb:
> Bin ein ganzes Stück weiter gekommen, ich bekomme jetzt vernünftige
> Datensatze "geliefert".

Na toll - und woran lag's?

von Peter M. (Gast)


Lesenswert?

Das
1
   static uint8_t gps_buf_cnt = 0;  // Buffer Character Counter
2
    static uint8_t gps_buf_wrf = 0;  // BUffer Write Flag

Sollte man in einer ISR nicht machen!

von Sven (Gast)


Lesenswert?

@ Michael:

weis ich nicht genau, scheint eine Timing-Sache zu sein - denn wenn ich 
im Mainloop eine Verzögerung "einbaue" (_delay_ms(100)) wird die Ausgabe 
instabiel. D.h. jeder 50. Datensatz wird unvollständig ausgegeben. 
Morgen schaue ich mir die Sache nochmal ganz genau an. Oft sieht man am 
Folgetag mehr als im Moment.

von Peter II (Gast)


Lesenswert?

Peter M. schrieb:
> Sollte man in einer ISR nicht machen!

und warum nicht?

von Peter D. (peda)


Lesenswert?

Verm

Sven schrieb:
> wird die Ausgabe
> instabiel

Vermutlich zerschießt eine nicht gezeigte Routine den gps_buf_cnt und 
dann geht das '$' flöten.

von Sven (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

Hier das komplette Programm, es läuft aber nicht korrekt. Es werden 
weiterhin nur fragmentierte und / oder unkorrekte Datensätze ausgegeben. 
Mir fällt dazu nichts mehr ein, und kann es mir nicht erklären.

Vielleicht bekomme ich ja noch einen Hinweis, Danke.

von Peter II (Gast)


Lesenswert?

Kommt denn das "HALLO WELT" richtig an?

wenn ja, teste ob es auch mehrfach hintereinander richtig ankommt

for( uint8_t i = 0; i < 10; ++i ) {
  SER_STR ("HALLO WELT"); CRLF;
}

von Sven (Gast)


Lesenswert?

@ Peter II: Hab ich eben gemacht. Ja, das Programm schreibt brav 10 mal 
HALLO WELT.
Als weiteres wenn man die Zeile _Delay_ms im Mainloop auskommentiert 
ändert sich auch die Fragmentierung der Ausgabe, Ebenfalls wenn man die 
gesetzten ms variiert.

von Peter II (Gast)


Lesenswert?

was hast du denn jetzt mit den Programm gemacht!

 if(gps_buf_rdy == 0 && UDR1 == '$')


was soll das? UDR1 darf man nur einmal auslesen, vorher war es besser.

von Sven (Gast)


Lesenswert?

@ Peter II: War ein verzweifelter versuch, denn ich bin auch immer davon 
ausgegangen das das Byte pfutsch ist wenn man einmal ausgelesen hat. 
Mache ich also wieder rückgängig und führe die gps_buf_rxc wieder ein.

von Peter D. (peda)


Lesenswert?

Mal eine völlig andere Sache:
Ich hatte auch mal Probleme, daß ein Programm verrückt spielte.
Der Grund war der Quarz im Low-Power Modus. Dieser Modus ist extrem 
empfindlich gegen Noise auf GND und VCC:

"This Crystal Oscillator is a low power oscillator, with reduced voltage 
swing on the XTAL2 output.
It gives the lowest power consumption, but is not capable of driving 
other clock inputs, and
may be more susceptible to noise in noisy environments. In these cases, 
refer to the “Full swing
crystal oscillator” on page 31."

Nach setzen der Fuses auf Full-Swing lief der AVR wieder stabil.

von Fehlerteufel (Gast)


Lesenswert?

1
ISR(USART1_RX_vect) {
2
...
3
    if(gps_buf_wrf == 1) // If Buffer Write Flag is set
4
    {
5
        gps_buf_cnt++;  // Increment Counter
6
        if(gps_buf_cnt > 98)   
7
        {
8
            gps_buf_wrf = 0; 
9
        }
10
        nmea[gps_buf_cnt] = UDR1; // Add character to nmea array
11
        ...

Wenn du schon feststellst das du außerhalb des Arrays schreibst solltest 
du auch nicht weiter arbeiten. Nachdem du das Flag gps_buf_wrf auf 0 
gesetzt hast, die ISR einfach per return verlassen.
Vermutlich wird dies der Grund für die gelegentlichen Abstürze sein. 
Denn wenn du ein carriage-return verpasst wird gps_buf_cnt bis auf 99 
laufen und du schreibst außerhalb des Arrays.

An dieser Stelle hast du UDR1 bereits das 2. Mal ausgelesen, sollte man 
auch nicht machen.
1
volatile char *zeiger;

zeiger muss weder volatile noch global sein, es reicht wenn du den 
Pointer in der main-Routine deklarierst.

von Peter II (Gast)


Lesenswert?

Fehlerteufel schrieb:
> Vermutlich wird dies der Grund für die gelegentlichen Abstürze sein.
> Denn wenn du ein carriage-return verpasst wird gps_buf_cnt bis auf 99
> laufen und du schreibst außerhalb des Arrays.

nein, denn wenn es 99 ist, dann setzt er ja gps_buf_wrf = 0; damit wird 
nichts weiter geschrieben.

von Martin M. (capiman)


Lesenswert?

@Peter II
Das ist jetzt nicht mehr so, jetzt wird nach setzen des Flags noch
geschrieben.

Alles in allem ist die heutige Version m.E. schlechter
als die gestrige Version.

      gps_buf_cnt++;  // Increment Counter
      if(gps_buf_cnt > 98)
        {
        gps_buf_wrf = 0;
        }
->    nmea[gps_buf_cnt] = UDR1; // Add character to nmea array
      if(UDR1 == '\r') // If character ist r (return)
        {
        gps_buf_wrf = 0; // Clear BUffer Write Flad
->      nmea[gps_buf_cnt] = '\0';  // Replace '\r' for '\0'
        gps_buf_rdy = 1;  // Set Buffer Ready to 1

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Martin Maurer schrieb:
> Das ist jetzt nicht mehr so, jetzt wird nach setzen des Flags noch
> geschrieben.

stimmt, dort hat er auch wieder rumgeändert. Es sollte dann zumindest 
auf >= 98 testen.

von Martin M. (capiman)


Lesenswert?

- Es werden jetzt Zeichen gelesen, ohne das geprüft wird,
  ob überhaupt welche da waren.

- Replace '\r' for '\0'
  wird nicht funktionieren, weil nicht das alte Zeichen,
  sondern irgendwas im UART-Empfangsbuffer benutzt wird.
  Damit gibt es auch nicht mehr sicher ein '\0' am Ende
  des Strings, was wiederum die Ausgabe im main beeinflusst...
  (um es harmlos auszudrücken...)

von lex (Gast)


Lesenswert?

Ganz ehrlich, das doch nur noch Rumgebastel hier.
"...gestrige Version besser als die heutige...", liest sich alles so 
alswürdest du nur noch wild rumprobieren, irgendwelche Flags einführen 
und hoffen dass irgendwie zufällig klappt.

So, das mag zwar manchen wie Kanonen auf Spatzen erscheinen, aber ich 
würd den ganzen Aufbau nochmal überdenken. Du solltest dir angewöhnen, 
alles viel modularer zu machen.

Besorg dir eine FIFO-gepufferte UART-Routine (z.b. Fleury) oder schreib 
selber um zu lernen.
Dann lässt du dir regelmäsig den Empfangspufferinhalt ausgeben, oder per 
Debugger, was dir eben zur Verfügung steht.

Wenn das steht kannst du deine NMEA-logik darauf aufsetzen. D.h. dein 
Parser schaut regelmäsig in den Puffer, holt sich chars falls vorhanden 
und sammelt diese erstmal bis ein Frame komplett ist.
(ich weiß nicht wie hochfrequent du diese Frames kriegst, evtl ist ein 
leicht anderer Ansatz notwendig).
Dann kannst du den kompletten Frame parsen oder verwerfen.

Aber wenn ich sehe dass der Parser in irgendwelchen Hardwareregistern 
rumpfuscht (UDR1) dann stellen sich mir die Nackenhaare auf.

Das wär mein Tipp. Aufwendiger ist das nur beim ersten Projekt. Danach 
hast du völlig entkoppelte Module die du nach belieben Verbinden kannst.
Den Code von Uart1 kannst du zB 1:1 für Uart0 übernehmen, da alle 
Registerzugriffe einer Hardwarekomponente nur im entsprechenden Modul 
passieren.

von Fehlerteufel (Gast)


Lesenswert?

Hier mal eine Version der ISR wie ich sie schreiben würde.
1
ISR(USART1_RX_vect) {
2
    static uint8_t gps_buf_wrf = 0;
3
    static uint8_t gps_buf_cnt = 0;
4
    static char c = '\0';
5
6
    c = UDR1;                              // read character from uart
7
8
    if ( c == '$' ) {
9
        if ( gps_buf_rdy == 0 )  {
10
            gps_buf_wrf = 1;               // Set Buffer Write Flag
11
            gps_buf_cnt = 0;               // Set Buffer Counter to 0
12
        } else {
13
            gps_buf_wrf = 0;               // Oups two '$' in one record!
14
            // TODO: Error Flag?
15
        }
16
    }
17
18
    if( gps_buf_wrf == 1 ) {               // Buffer ready to write?
19
        if( gps_buf_cnt < 99 ) {
20
            nmea[gps_buf_cnt] = c;         // Add character to nmea array
21
22
            if( c == '\r' ) {              // If character ist r (return)
23
                gps_buf_wrf = 0;           // Clear Buffer Write Flag
24
                nmea[gps_buf_cnt] = '\0';  // Replace '\r' for '\0'
25
                gps_buf_rdy = 1;           // Set Buffer Ready to 1
26
            }
27
28
            gps_buf_cnt++;                 // Increment Counter
29
        } else {
30
            gps_buf_wrf = 0;               // More bytes as expected!
31
            // TODO: Error flag?
32
        }
33
    }
34
}

Änderungen:
Es gibt nun eine weitere Möglichkeit eine Fehler zu erkennen, nämlich 
wenn in dem NMEA-Datensatz weiter $-Zeichen auftauchen kann ich recht 
sicher davon ausgehen das ich mindestens gerade ein carriage-return 
verpasst habe.

Dann wurde die Zählvariable gps_buf_cnt von int8_ auf uint8_t geändert. 
Eine negative Zahl im Array-Index bedeutet meistens Ärger, deshalb 
vermeide ich so etwas eigentlich immer.

Peter II schrieb:
> Es sollte dann zumindest auf >= 98 testen.
1
if( gps_buf_cnt < 99 )
Das ist IMHO die bessere Variante, so kann ich auch hingehen und die 99 
durch ein #define ersetzen um ganz bequem Array-Größe und Fehlerabfrage 
verändern.

Dann natürlich UDR1 in einer Variablen zwischengespeichert.

von Peter II (Gast)


Lesenswert?

Fehlerteufel schrieb:
> Hier mal eine Version der ISR wie ich sie schreiben würde.

> static char c = '\0';
> c = UDR1;

würde ich aber als

char c = UDR1;

schreiben, static macht dafür keinen sinn.

von Fehlerteufel (Gast)


Lesenswert?

Fehlerteufel schrieb:
>
1
> if ( c == '$' ) {
2
>         if ( gps_buf_rdy == 0 )  {
3
>             gps_buf_wrf = 1;               // Set Buffer Write Flag
4
>             gps_buf_cnt = 0;               // Set Buffer Counter to 0
5
>         } else {
6
>             gps_buf_wrf = 0;               // Oups two '$' in one 
7
> record!
8
>             // TODO: Error Flag?
9
>         }
10
>     }
11
>

Hier hat sich leider ein Fehler eingeschlichen! :(
Das wird so nicht klappen, die Abfrage sollte besser an anderer Stelle 
passieren.

von Sven (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Nochmal,

dieser Code macht genau das was ich will, perfekte ausgaben - bis das 
Programm stehen bleibt. Ich falle fast vom glauben ab, ich programmiere 
seit 15 Jahren mit AVR's und so etwas ist mir noch nie passiert. Ich 
kann das einfach nicht nachvollziehen.

von holger (Gast)


Lesenswert?

>      gps_buf_cnt++;                          // Increment Buffer Counter

Den sehe ich, aber wo wird der Counter jemals zurückgesetzt?
Mach besser mal ne Abfrage ob der Counter für das jeweilige
Array auch noch IM Array ist und nicht woanders.

von Sven (Gast)


Lesenswert?

@ Holger:

Der Counter wird zurückgesetzt wenn die Startbedingung erfüllt ist.

if(gps_buf_rxc == '$')
  {
  gps_buf_wrf = 1;  // Set BUffer Write Flag
  gps_buf_cnt = 0;  // Set Buffer Counter to 0
  gps_csm_flg = 0;
  gps_csm_clc = 0;
  }

von Markus (Gast)


Lesenswert?

Ist ausgeschlossen, dass mal '$\r' empfangen wird?

Denn
   s0[0] = nmea[gps_buf_cnt - 2];
würde dann nämlich auf den Speicher vor nmea greifen, was je nach Lage 
von nmea ein ungültiger Speciherbereich sein kann.

Hier wäre dann ein Check auf gps_buf_cnt>=2 sinnvoll.

von holger (Gast)


Lesenswert?

>Der Counter wird zurückgesetzt wenn die Startbedingung erfüllt ist.
>
>if(gps_buf_rxc == '$')

Und wenn du dieses Zeichen nicht mitbekommst läuft
der counter fröhlich weiter.

von Peter II (Gast)


Lesenswert?

holger schrieb:
> Und wenn du dieses Zeichen nicht mitbekommst läuft
> der counter fröhlich weiter.

nein:
1
  gps_buf_cnt++;                          // Increment Buffer Counter
2
      if(gps_buf_cnt >= 83)                    //  
3
        {
4
        gps_buf_wrf = 0;

es wird nach einer Maximalen anzahl von zeichen aufgehört.

von Peter D. (peda)


Lesenswert?

Ich würds trotzdem mal defensiv programmieren, also direkt vor dem 
Zugriff auf Gültigkeit prüfen:
1
if( i < (sizeof( x ) / sizeof( x[0] ))){
2
  x[i] = data;
3
}

von Peter D. (peda)


Lesenswert?

P.S.:
Mein Post bezüglich Oszillator hast Du gelesen?

von Sven (Gast)


Lesenswert?

@ Peter Dannegger: Das mit dem Ozillator habe ich gelesen, ich werde das 
morgen nochmal checken - heute bin ich zu müde dazu.

Allgemein:

Ansonsten läuft das Programm jetzt wie gedacht, obwohl ich zugeben muss 
das die ISR-Routine die länge ist die ich je geschrieben habe, sowohl 
was die Codelänge angeht, wie auch die benötigte Zeit ;-)
Ich frage mich ob das alles wirklich alles die ISR erledigen lasse, aber 
meine Idee war halt einen korekten Datensatz zur Verfügung zu haben wenn 
das Buffer Ready Flag im Mainloop gesetzt ist. Immerhin werden die Bytes 
ja eh in der ISR durchlaufen, warum nicht auch gleich die Prüfsumme 
berechnen und Überprüfen.

Dann habe ich bemerkt, das einige Datensätze häufiger ausgegeben werden 
als andere - das ist halt Programmbedingt. ALso wäre eine synchonisation 
ganz gut. Mein GPS liefert Datensätze in folgender Reihenfolge GGA, GSA, 
GSV, GLL, RMC, VTG. Diese werden jetzt der Reihenfolge nach "requested", 
und zwar dadurch wenn nach dem 5 Zeichen die Prüfsumme entsprechend ist 
(gps_rot). Wenn das Buffer Ready Flag im Mainloop gestezt ist, kann ich 
also sicher sein das der Datensatz korrekt ist und weiss auch um welchen 
Datensatz es sich handelt, was Abfragen nach dem Datensatz beim Parsen 
unnötig macht. Ja es werden viel Bytes "verworfen" das ist in diesem 
Fall aber egal denn es kommen ja immer neue nach.

Jetzt habe ich noch folgendes gemacht, bei jeder Abbruchbedingung in der 
ISR wird ein Portbit gesetzt (oldschoolige Visualisierung mittels 
LED's). Ich werde das Programm über Nacht laufen lassen und wenn morgen 
eine LED leuchtet wurde eine Abbruchbedingung erfüllt. Somit kann ich 
sehen ob die ISR irgendwann einmal aus dem "Ruder" gelaufen ist, und 
wenn ja wo. Ferner werde ich den Output mitloggen und morgen nachsehen, 
ob Unregelmässigkeiten aufgetreten sind. Und wenn das ein paar Stunden 
unauffällig durchläuft kann man davon ausgehen, das es funktioniert.

Auf jeden Fall möchte ich jeden "Antworter" in diesem Thread danken, 
habe viele Hinweise und Anregungen erfahren. Was wohl auch zum Erfolg 
geführt hat, nochmals vielen Dank.

Wenn das jetzt läuft prima, dann habe ich schon die nächste Idee. Ich 
benutzte den 1284p weil er zwei USART's hat. Mit dem USART 0 gebe ich 
die Daten auf das Terminal aus, welche ich von USART 1 erhalte, 
dazwischen ist natürlich der Code. Vielleicht geht es sogar das ich nur 
einen USART verwende. RCX um Daten vom GPS zu empfangen und TXC und 
Daten (zum Loggen) zu senden. Vielleicht geht es sogar auch die Baudrate 
dazwischen umzuschalten??? RXCIE müsste beim Senden natürlich 
deaktiviert werden. Ich möchte nicht gerne einen Software UART 
verwenden.

Nochmals Danke für die Hilfe.

von lex (Gast)


Lesenswert?

Aus Interesse: wie hochfrequent gibt denn dein GPS seine Frames aus? 
Reden wir hier von ms-Intervallen, 100ms oder gar Sekunden? Ist die 
Zykluszeit variabel oder stets gleich?
 (sorry falls das bereits wo erwähnt wird, der Thread ist mittlerweile 
recht lang)

von Sven (Gast)


Lesenswert?

Das GPS gibt vier mal pro Sekunde GGA, GSA, GSV, GLL, RMC und VTG bei 
38400 Baud aus. Die gehen dann in den mC (Programm) und werden in o.g. 
Reihenfolge wieder auf dem Terminalprogramm (115.200 Baud) ausgegeben 
und zwar alle 6 Datensätze einmal pro Sekunde.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ober schrubst du das GPS-Teil läuft mit 4800 Baud.

von Martin M. (capiman)


Lesenswert?

Sven schrieb:
> Das GPS gibt vier mal pro Sekunde
> ...
> zwar alle 6 Datensätze einmal pro Sekunde.

Die Logik der Datenreduktion war im letzten Code
m.E. aber auch noch nicht drin. Ergibt sich
das jetzt durch Zufall so?

Nachtrag: Erklärst du mir nochmal,
was es mit

volatile uint8_t gps_rot[6] = {86,80,66,75,85,82};

auf sich hat? Ist das der Grund für die Datenreduktion?
Ist es richtig, dass du die berechnete CRC zur
Filterung der Pakete benutzt?

: Bearbeitet durch User
von Sven (Gast)


Lesenswert?

Martin schrieb:

Erklärst du mir nochmal, was es mit

volatile uint8_t gps_rot[6] = {86,80,66,75,85,82};

auf sich hat?

Ist es richtig, dass du die berechnete CRC zur Filterung der Pakete 
benutzt?
JA

Die Zahlen sind die sich ergebenen Prüfsummen bis zum 5. Zeichen des 
Datensatzen. Die sind ja immer gleicht weil GPGGA uzw. ja bekannt ist. 
Ich will als nächsten Datensatz GPGAA (xor also 86) haben, wenn das bein 
5. Zeichen niht der Fall ist breche ich ab. So bekomme ich also immer 
nur Datensätze in definierter Reichenfolge geliefert. 86,80,66,75,85,82 
legt dann auch gleich die Reihenfolge fest, bzw. die Datensätze welche 
ich überhaupt haben will (in diesem Fall zum man die entsprechenden 
Zahlen halt weglassen.

Und ja, die Interruptroutine ist ziemlich lang. Aber ich habe halt wert 
darauf gelegt, das wenn gps_buf_rdy = 1 ist mir ein geprüfter Datensatz 
zur Verfügung steht. Ich weiss um was für einen datensatz es sich 
handelt, Variable rot, bracuhe das dan hinterher nicht zu ermitteln. Es 
hat sich nämlich gezeigt, ohne diese "Request Rotation" das einige 
Datensatztypen öfter Ausgegeben weden als andere, was in meinem Fall 
nicht gewünscht ist.

Tatsache ist auch das somit ca. 75 der Daten verworfen werden, aber wie 
geschrieben es kommen ja immer wieder welche nach.

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.