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!
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.htmhttp://de.wikipedia.org/wiki/NMEA_0183
>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
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?
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...
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.
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?
@ 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.
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.
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
volatilechar*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
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
staticuint8_tgps_buf_wrf=0;
3
staticuint8_tgps_buf_cnt=0;
4
staticcharc='\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.
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.
@ 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?
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.