Guten Tag alle zusammen,
ich benutze das Board AVR-MT-128 von ATmel und den GPS-Empfänger
EM-406A. Der GPS-Empfänger übermittelt die Daten (NMEA 1083 Protokoll)
über serielle Schnittstelle RS232 an das Board. Hier wird dann z.B. die
UTC und die aktuelle Position auf dem LCD-Display angezeigt.
Ich programmiere mit AVR Studio 5.1 in C.
Die übermittelten Zeichen werden sequentiell an das Board geschickt.
Dieses liest das aktuelle Zeichen ein. Wenn das Zeichen '$' detektiert
wird, werden die nachfolgenden Zeichen in einen String gespeichert, bis
erneut ein '$' detektiert wird. Das Zeichen '$' markiert den Start eines
vollständigen Datensatzes des Protokolls. Der String wird dann mit einem
bestimmten Teilstring verglichen. Ist dieser vorhanden, werden bestimmte
Werte aus dem String dann auf dem Display angezeigt (z.B. aktuelle
Position).
Nun zu meinem Problem:
Nach Spannungsverlust beim Board werden nicht mehr die richtigen Daten
angezeigt. Ein erneutes Aufspielen meines Programmes auf das Board
beseitigt den Fehler nicht. Erst wenn ich von ATmel ein Demo-Code auf
das Board spiele und dann meinen Code, dann werden wieder korrekte Daten
angezeigt.
Demo-Code:
http://www.watterott.com/de/AVR-MT-128
Woran könnte das liegen?
Gruß Stefan
Da die Glaskugel gerade zum Polieren weg ist, kannst du vielleicht noch
einige Zusatzinfos geben: Was bedeutet "falsche Anzeige"? Chinesische
Schrift? Oder Uhrzeit falsch?
Der Fehler deutet auf eine fehlende oder falsche Initialisierung eines
der Bauteile hin. Speziell zum Thema "LCD" geistern hier diverse
optimistische (um es höflich auszudrücken) Beispiele durch das Forum.
Wobei mir unverständlich ist, warum alles immer wieder neu erfunden
werden muss.
Ins Blaue geraten:
Da wird halt dein Code irtgendeine Initialisierung nicht machen, die der
Demo-Code schon macht.
Um dem auf die Spur zu kommen, würde ich mir als erstes mal die
Datensätze ansehen, so wie sie vom GPS kommen, nachdem dein Board
stromlos war. Die dann vergleichen mit dem was daherkommt, nachdem das
Demo-Programm die Initialisierung vervollständigt hat. Vielleicht führt
das zu was, vielleicht auch nicht - aber einen Versuch ist es erst mal
wert rauszufinden, wo da die Unterschiede liegen.
while(!(UCSR1A&(1<<UDRE1)));// Warte, bis Buffer bereit ist zum Empfangen
153
154
UDR1=data;// Sende Daten
155
}
156
157
/*
158
** Funktionen zur Stringverarbeitung
159
*/
160
161
162
// Initialisiere Strings
163
voidStr_Init(char*str,charinit)
164
{
165
intj=0;
166
while(j<strlen(str))
167
{
168
str[j]=init;
169
j++;
170
}
171
}
172
173
// Zähle Kommas in String
174
intStr_Komma(char*str,intn)// n: Anzahl der Kommas entspricht dem Wert, der angezeigt werden soll z.B. Uhrzeit
175
{
176
intsumme=0;
177
inti=0;
178
for(i=0;i<strlen(str);i++)
179
{
180
if(str[i]==',')
181
{
182
summe++;// Anzahl der Kommas
183
}
184
if(summe==n)
185
{
186
returni+1;// Gebe die 1. Stelle nach dem Komma zurück
187
}
188
}
189
}
190
191
// Buzzer
192
voidBuzzer(void)
193
{
194
BUZZ1_LOW;
195
BUZZ2_HIGH;
196
delay_us(125);
197
BUZZ2_LOW;
198
BUZZ1_HIGH;
199
delay_us(125);
200
}
Es werden falsche Daten aus dem String gezeigt. Vorher wird zum Beispiel
UTC: hh:mm:ss auf dem Display sekündlich aktualisiert und angezeigt.
Nach dem Spannungsverlust wird auch weiterhin UTC: : : dargestellt.
Allerdings wird zwischen den Doppelpunkten nun nicht mehr die korrekte
Uhrzeit angezeigt, sondern ein Schwall von Daten aus dem String.
Woran kann es liegen? Was verändert sich dadurch, wenn ich zunächst ein
Demo-Code aufspiele und dann meinen Code? Ich bin ziemlich ratlos und
wäre dankbar über jedigliche Hilfe.
Gruß Stefan
Kommen denn nach dem Stromausfall noch korrekte Datensätze aus dem GPS
Modul?
Wenn nein, dann stimmt die Initialisierung des GPS Moduls nicht.
Prüfen indem man die Daten zum PC sendet (Terminalprogramm)
Puh.
Du hast da ein ganz schönes Durcheinander in der Stringbearbeitung
veranstaltet.
Dein
char Str[100];
ist zuallererst mal uninitialisiert.
Und daran ändert sich auch in weiterer Folge nichts. Du hast nirgends in
deinem Code die Gewissheit, dass du es mit einem gültigen, sauberen
String zu tun hast.
Wenn ich dir einen Vorschlag machen darf: Vergiss solche Funktionen wie
Str_Init. Die wiegen dich nur fälschlich in Sicherheit, machen aber
nicht das was du erwartest.
Kümmere dich bei der Stringverarbeiteung immer selbst darum, dass du am
Ende eines Strings ein \0 Zeichen hast, welches den String terminiert.
Solange dieses \0 Zeichen nicht an der richtigen Stelle ist, kannst du
keine Funktionen wie strlen, strstr, etc. aufrufen!
zb hier
1
Ch=UART_Receive();// Aktuelles Zeichen einlesen
2
3
// Zeichen zwischen Anfang und Ende einlesen
4
if(bed==1&&Ch!='$'&&Ch)
5
{
6
Str[k]=Ch;
7
k=k+1;
8
}
9
10
// Letztes Zeichen einlesen
11
if(bed==1&&Ch=='$')
12
{
mach hinten an den String EXPLIZIT das \0 Zeichen rein und du hast es
mit einem gültigen String zu tun, den du mit strstr untersuchen kannst
1
// Letztes Zeichen einlesen
2
if(bed==1&&Ch=='$')
3
{
4
Str[k]='\0';
5
...
Genauso hier
// Start zum Einlesen des Strings
if (bed == 0 && Ch == '$') // Zeichen $ leitet Datensatz ein
{
Str_Init(Str,'0'); // String Inhalt mit '0' initialisieren
[/C]
welches offenbar benutzt wird, um das erste mal auf den '$' zu
synchronisieren. Du kannst hier nicht Str_Init aufrufen! Du hast noch
keinen gültigen String in Str. Ganz abgesehen davon, was soll das
bringen, da überall ein '0' reinzuschreiben? Das braucht kein Mensch.
1
// Start zum Einlesen des Strings
2
if(bed==0&&Ch=='$')// Zeichen $ leitet Datensatz ein
3
{
4
Str[0]='$';
5
Str[1]='\0';
6
k=1;
7
}
Im übrigen ist dein Code ziemlich unübersichtlich. Was soll 'bed'
bedeuten? Offensichtlich ist das eine Variable, mit der du festhalten
willst, ob sich dein Programm mit den GPS Daten synchronisiert hat. Dann
nenn die Variable auch so und mach dir 2 große Blöcke in die Schleife:
* was soll mit dem Zeichen passieren, solange noch nicht synchronisiert
wurde
* was soll mit dem Zeichen passieren, wenn sich das Programm auf den
Anfang des NMEA Datensatzes synchronisiert hat.
Es gibt keinen Grund, dass du da 25 if hast, in denen du ständig bed
abfrägst. Die erste Frage nach erhalt eines Zeichens lautet: Sind wir
synchronisiert, ja oder nein.
1
Synchronized=FALSE;
2
3
while(1){
4
5
....
6
7
Ch=UART_Receive();
8
9
if(!Synchronized){
10
11
if(ch=='$'){
12
Str[0]='$';
13
Str[1]='\0';
14
k=1;
15
Synchronized=TRUE;
16
}
17
}
18
19
else{
20
if(Ch!='$'){
21
Str[k++]=Ch;
22
23
else{
24
Str[k]='\0';
25
26
....DiesenDatensatzauswerten
27
28
....undneuenanfangen
29
Str[0]='$';
30
Str[1]='\0';
31
k=1;
32
}
33
}
34
}
(und wenn man sich das mal im Detail überlegt, benötigt eigentlich kein
Mensch das '$' an der ersten Position im String. Denn dieses '$' steht
sowieso IMMER String. Und wenn es sowieso IMMER vorhanden ist, dann
braucht das auch keiner. Denn ob du "$GPGGA" in diesem String suchst,
oder nur "GPGGA" macht auch keinen wirklichen unterschied mehr. Zumal du
weißt, dass dieses GPGGA sowieso am Stringanfang vorkommen muss. D.h. du
wirst da nicht strstr benutzen, sondern strncmp um abzutesten, ob die
ersten 4 Buchstaben im String "GPGGA" lauten. In der traditionellen
Kette "Eingabe - Verarbeitung - Ausgabe" gibt es 3(!) Stellen, an denen
die Dinge schief gehen können. Ja, da gehört auch die Eingabe mit dazu.
Also muss man das kontrollieren!
Soweit so gut. Du hast nämlich noch ein weiteres Problem: Deine
Auswertung bzw. das Hinschreiben auf das LCD kostet alles Zeit. In
dieser Zeit kann aber das GPS schon beginnen, den nächsten NMEA
Datensatz rauszuschicken. Sobald du das erste Zeichen nicht richtig
mitbekommst, ist deine ganze Auswertung gefährdet. D.h. der ganze Ansatz
mit Polling der UART ist schon mal nicht der Schlaueste. Wenn du eine
UART Gegenstelle nicht am Senden hindern kannst, dann brauchst du eine
Interrupt-getriebene UART Empfangsroutine mit einer FIFO, welche die
Zeichen mal kurz zwischenspeichert, solange dein Programm noch mit der
Auswertung beschäftigt ist.
Und dann: ehe du dich an die Auswertung machst, SIEH DIR DEN STRING AN,
DEN DEIN PROGRAMM EMPFANGEN HAT. Ich hab dir das damals schon ans Herz
gelegt und ich tu es heute wieder! Wenn dein String, den du aus den
Daten vom GPS empfängst schon nicht der ist, den du erwartest, dann
kannst du auswerten bis du schwarz wirst - da kommt nichts gescheites
raus! Also muss man das als allererstes kontrollieren! Was genau
empfängt dein Programm vom GPS? Das ist die Frage, der du dich stellen
musst. Ehe du das nicht weißt, brauchst du dich gar nicht daran
versuchen mit den Daten (von denen du nicht weißt wie sie aussehen)
irgendwas anzufangen.
Vielen Dank erst einmal für eure hilfreichen Kommentare!
@ Karl Heinz Buchegger
Was wäre denn die einfachste Möglichkeit sich den Inhalt des Strings
anzeigen zu lassen?
Gruß Stefan
Ich wollte eigentlich fragen, ob ich mir den Inhalt des Strings am PC
anzeigen lassen kann. Ich würde den Inhalt des Strings gerne als ganzes
sehen. Auf dem Display kann ich ja nur 16 Zeichen pro Zeile anzeigen
lassen (Display hat 2 Zeilen).
Stefan O. schrieb:> Ich wollte eigentlich fragen, ob ich mir den Inhalt des Strings am PC> anzeigen lassen kann.
Tja.
Wenn du nicht noch eine UART hast oder einen Debugger, mit dem du den µC
online debuggen kannst, dann ... gar nicht
> Ich würde den Inhalt des Strings gerne als ganzes> sehen. Auf dem Display kann ich ja nur 16 Zeichen pro Zeile anzeigen> lassen (Display hat 2 Zeilen).
Tja. Damit wirst du leben müssen, dass du nicht mehr Möglichkeiten hast.
Du kannst nur das Beste daraus machen. (Aber wenn die ersten 10 Zeichen
des Strings gut aussehen und die letzten 10 Zeichen, dann ist die
Wahrscheinlichkeit schon sehr gross, dass der Rest dazwischen auch
stimmt. Und bei 2 Zeilen a 16 Character, kannst du dir in den beiden
Zeilen ja diese 'Stellvertreter' ausgeben lassen)
@ Karl Heinz Buchegger
Das Anzeigen des Strings auf dem LCD-Display habe ich getestet und es
funktioniert gut. Der String wird mit dem richtigen Inhalt beschrieben.
Ich habe es in HTerm verglichen.
Mein Programm habe ich so wie du es mir vorgeschlagen hast
umstrukturiert. Der Initialisierungsfehler ist nun behoben :-).
Mein Problem ist nun, dass auf dem LCD-Display z.B. die Uhrzeit nun 4
Sekunden lang kontinuierlich anzeigt wird und dann für 6 Sekunden stehen
bleibt. Danach erst wird wieder die richtige Uhrzeit für weitere 4
Sekunden kontinuierlich angezeigt, dann wieder Stillstand, usw..
Könnte dies daran liegen, wie du schon vermutet hast, dass ich keine
Interrupt-getriebene UART Empfangsroutine benutze, sondern nur über
Polling des UARTs? Oder könnte es auch noch ein anderer Grund sein?
Anbei der Code.
Stefan,
gemeint ist mit "blockierend" die vielen Delay.
Auf einem µC arbeitet man anderes -- eventorientiert, d.h.
Liegt ein RS232-Zeichen im Empfangspuffer vor, dann..
Wurde eine Taste gedrückt, losgelassen, lange gedrückt, usw.
Ist eine Zykluszeit, z.B. 1ms, abgelaufen, dann update das LCD und
bearbeite Prozess 1, 2 ...
Schreibe ein Einfach zulese des Programm, verwende Funktionen zur
Modularisierung und einfache zu lesende Programmanweisungen..
Kurz um Warten ist sehr schlecht.
Blockierend bedeutet, es wird ueberall gewartet bis etwas beendet ist.
Zum einen ist das schade fuer die Zeit, zum anderen kann ja etwas worauf
man wartet nicht eintreten. Sowas ist dann extrem schwierig zu debuggen,
der Prozessor wartet irgend wo.
Besser ist mit einer oder mehreren Zustandsmaschinen zu arbeiten. Und
dann wird genau an einem Ort im ganzen Code gewartet, das waere dann der
schnelle Poll-Loop im Main, der die verschiedenen Zustaende prueft und
abarbeitet. Am Ende dieses Loops kann man dann, falls gewuenscht auch in
den Sleep gehen.
Die Zusatndsmaschine ist viel einfacher zu debuggen, denn man kann ja
den Zustand zB per serieller Schnittstelle oder per LED
abfragen/aufgeben lassen.
Die einzelnen Interrupts machen nur was minimal zu tun ist, setzten eine
Boolean und lassen den Prozessor im Main Lop das Passende erledigen.
Die delays werden in meinem Programm hauptsächlich nur bei der
Initialisierung und dem Willkommensgruß verwendet. Was hat das damit zu
tun, dass auf dem LCD-Display z.B. die Uhrzeit nun 4
Sekunden lang kontinuierlich anzeigt wird und dann für 6 Sekunden stehen
bleibt? Dies wiederholt sich dann periodisch...
> Ch = UART_Receive(); // Aktuelles Zeichen einlesen
Das blockiert bis vom GPS das nächste Zeichen kommt
Und solange nicht ein kompletter Datensatz korrekt empfangen wird, gibt
es auch keine Updates.
Ich sagte doch schon: lass dir endlich mal anzeigen, was du eigentlich
empfängst. Du glaubst es ja nicht, dass man mit Stochern im Nebel nicht
weiter kommt. Dann musst du es eben auf die harte Tour lernen.
Das wichtigste ist ja immer, dass es eine Wilkommensnachricht gibt :-)