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.
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?
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.
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.
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?
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.
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.
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.
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.
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
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!
Also mein ATmega1284P (@8 oder 20 MHz) komuniziert schon seit jeher nur mit 115200Baud, ohne Probleme. Da horcht aber auch ein PC...
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)
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
>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
Läst du dir im terminalprogramm zur Kontrolle auch immer die roh-Daten anzeigen???
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.
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.
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.
@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
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.
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)
@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
Wenn das der wirkliche Code ist, müßte doch jede Zeile im Terminalprogramm mit '$' beginnen.
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.
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.
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.
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""
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.
Peter II schrieb: > Also stell uns bitte das komplette Programm zur Verfügung. Und auch gleich den Build-Output und die lss-Datei.
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.
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?
Sven schrieb: > Bin ein ganzes Stück weiter gekommen, ich bekomme jetzt vernünftige > Datensatze "geliefert". Na toll - und woran lag's?
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!
@ 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.
Verm Sven schrieb: > wird die Ausgabe > instabiel Vermutlich zerschießt eine nicht gezeigte Routine den gps_buf_cnt und dann geht das '$' flöten.
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.
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; }
@ 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.
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.
@ 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.
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.
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.
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.
@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
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.
- 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...)
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.
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.
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.
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.
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.
> 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.
@ 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; }
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.
>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.
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.
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 | }
|
@ 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.
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)
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.