Forum: Mikrocontroller und Digitale Elektronik GPS- Signal über RS232 einlesen (Atmega 644)


von Stafen P. (Gast)


Lesenswert?

Hallo Zusammen,

Folgendes Problem beschäftigt mich:
Im Unterricht wurde mir der Auftrag aufgebrumt ein GPS Signal (NMEA 0183 
Code) mit einem Atmega 644 einzulesen und über I2C-Bus die 
Positionsdaten und die Uhrzeit auf ein Display wieder zu geben. Das GPS- 
Gerät ist ein Garmin GPS 12 personal Navigator. Der µC befindet sich auf 
einem evaluation board von pollin.
Also ich möchte den Code über die RS232 einlesen.Das wären die Pins PD0 
und PD1 am µC.

Mir fehlt allerdings jetzt jeglicher ansatz wie ich da vorgehen muss. 
WIe kann ich das Signal einlesen und verarbeiten?

Verarbeitet bekomme ich die Daten eventuell noch alleine, allerdings 
müsste ich sie dafür erst mal in den µC bekommen.

Ich weiß es gibt da schon etliche Beiträge zu diesem Thema bzw. 
ähnlichen, aber ich habe bisher nciht das passende gefunden wo mit ich 
was anfangen könnte.
Ein kleines Beispielprogramm wäre natürlich super.

Ich hoffe ihr könnt mir helfen.

Gruß Stefan

von maximite TFT (Gast)


Lesenswert?

Entweder ist dein " Auftraggeber" verstrahlt, oder du bist völlig fehl 
am Platze.

Welche Schule ist das ?

von Wolfgang (Gast)


Lesenswert?

Stafen P. schrieb:
> Also ich möchte den Code über die RS232 einlesen.Das wären die Pins PD0
> und PD1 am µC

Na bestimmt nicht. Der µC kann nur 5V-Signale verarbeiten. Die 
RS232-Signale vom GPS-RX sind mindestens invertiert und sollten zwischen 
-12V und +12V (evtl auch weniger) hin und her schalten.

von spess53 (Gast)


Lesenswert?

Hi

>Mir fehlt allerdings jetzt jeglicher ansatz wie ich da vorgehen muss.
>WIe kann ich das Signal einlesen und verarbeiten?

-genügend großen Puffer anlegen
-UART Initialisieren
-warten bis ein '$' vorbei kommt
-Zeichen in Puffer einlesen bis CR/LF kommt
-Puffer decodieren

MfG Spess

von Jobst M. (jobstens-de)


Lesenswert?

Wolfgang schrieb:
> Die
> RS232-Signale vom GPS-RX sind mindestens invertiert und sollten zwischen
> -12V und +12V (evtl auch weniger) hin und her schalten.

Falsch, die meisten haben gar keine RS232, viele haben TTL-Pegel und 
sind nicht invertiert. Einige haben sogar nur 3,3V. Das sollte der TO 
allerdings in Erfahrung bringen.

Schau Dir erst einmal an, wie die UART im µC funktioniert. Schick Daten 
vom µC zum PC (Terminalprogramm) und zurück.

Wenn Du das hast:

1 - Auf <cr> im Datenstrom warten
2 - Daten bis zum nächsten Komma einlesen.
3 - Eingelesene Daten = "$GPGGA" ? Wenn nicht, gehe zu 1
4 - Daten bis zum nächsten Komma einlesen
5 - Eingelesene Daten sind die Uhrzeit
6 - Daten bis zum nächsten Komma einlesen
7 - ...
:
:
N - Gehe zu 1


Gruß

Jobst

von Wolfgang (Gast)


Lesenswert?

Jobst M. schrieb:
> Falsch, die meisten haben gar keine RS232, viele haben TTL-Pegel und
> sind nicht invertiert.

Es geht hier nicht um "die meisten" sondern um einen Garmin GPS 12. Das 
von Garmin gelieferte Datenkabel zum Anschluss an den PC verwendet 
keinen Pegelkonverter, sondern stellt eine direkte Verbindung zur 
RS232-Schnittstelle eines PC dar. Eine direkte Verbindung zum µC geht 
allenfalls mit Soft-UART mit SW-Invertierung.

von M.N. (Gast)


Lesenswert?

Abgesehen davon, dass das GPS12 uralt und ensprechend taub und lansgam 
ist, ist die Algorithmik dahinter eher trivial, wenn man schon mal was 
mit µC gemacht hat. Wenn nicht, muss man sich eben einarbeiten.

Die NMEA-Daten kommen mit 4800 Baud, was eher langsam ist. Der Decoder 
wird sich also mit praktisch jeder GPS-Maus verwenden lassen, die noch 
RS232 ausgibt.

Folgender Ansatz, wenn man vom Urschleim anfangen will:
* LED-Blinken lassen
* "Hallo Welt!" auf Display ausgeben lassen
* Sich mit UART und seinen Interrupts beschäftigen (den MAX232 als 
Pegelwandler wirst du brauchen, sofern nicht auf dem Board vorhanden 
ist)
* Jedes mal, wenn ein Zeichen empfangen wird, die LED aufblinken lassen
* Die empfangenen Datensätze sind max. 80 Byte lang zzgl. CR+LF
* Startbedingung wenn ein $ empfangen wurde
* Bytes ab da im Puffer sammeln
* Stopbedingung CR+LF, damit Flag setzen, dass ein Datensatz empfangen 
wurde

* In der Hauptschleife bei Auftauchen des Flags mit einem "Parser" den 
Datensatz im Puffer durchgehen. Fängt er mit $GPRMC kannst du es weiter 
auswerten, wenns was anderes ist, darfst du es verwerfen.

* Uhrzeit und Position haben ein festes Format.
* Dabei auch das Gültigkeitsbit auswerten.

* Daten formatieren und zum Display schauffeln.

* für den Anfang schadet es nicht, sich die Daten auch mal nur aufm 
Terminal anzusehn und evtl. auf dem PC mit nem kleinen Script 
auszuwerten (Basic, MatLab etc..)

Also ich würd sagen, wenn man es konsequent durchzieht, in zwei Wochen 
machbar.


Pflichtlektüre:
http://www.kowoma.de/gps/
http://www.kh-gps.de/ (Dort gibts auch Programmbeispiele)
Das wäre z.B. ein guter Einstieg: http://www.kh-gps.de/gpsdec1.htm

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang schrieb:
> Jobst M. schrieb:
>> Falsch, die meisten haben gar keine RS232, viele haben TTL-Pegel und
>> sind nicht invertiert.
>
> Es geht hier nicht um "die meisten" sondern um einen Garmin GPS 12. Das
> von Garmin gelieferte Datenkabel zum Anschluss an den PC verwendet
> keinen Pegelkonverter, sondern stellt eine direkte Verbindung zur
> RS232-Schnittstelle eines PC dar.

Na dann passt es doch wunderbar.
der 644 kriegt einen MAX232 verpasst, dann kann er wahlweise
* den Garmin mit dem PC verbinden und sich mal ansehen, was der so 
sendet
* den 644 mit dem PC verbinden und damit erst mal seine Fähigkeiten im 
programmieren der UART verbessern
* den Garmin mit dem 644 verbinden und den 644 die Daten empfangen 
lassen (nach diversen Vorstudien, die er zuerst mit PC-µC gemacht hat)


Wo liegt das Problem? Hau rein!

Wenn ich allerdings die Wortwahl bedenke
> Im Unterricht wurde mir der Auftrag aufgebrumt
dann schwant mir übles.

: Bearbeitet durch User
von Stefan P. (Gast)


Lesenswert?

So sorry hatte die letzten Tage kein Internet.
Also erstmal vielen Dank für die raschen Antworten.
Die RS232 Schnittstelle ist mit MAX232 schon auf dem Board vorhanden, 
das ich da habe.
Eine Frage hab ich da aber noch. Wie steuer ich die Schnittstelle denn 
an? Die werde ich ja sicher nicht mit "checkbit PIN..." oder "setbit 
PIN..." ansteuern können.

von spess53 (Gast)


Lesenswert?

Hi

>Eine Frage hab ich da aber noch. Wie steuer ich die Schnittstelle denn
>an? Die werde ich ja sicher nicht mit "checkbit PIN..." oder "setbit
>PIN..." ansteuern können.

Dafür hat der ATMega644 eine USART. Oder wenn es ein ATMega644P oder PA 
ist, sogar zwei.

MfG Spess

von Aeh (Gast)


Lesenswert?

-Ansteuerung UART nach Datenblatt des Mega644.
-Zerscheibeln und interpretieren der Daten nach NMEA ( frag gurggell )

von Stafen P. (Gast)


Lesenswert?

achso oke dann werd ich mich da mal schlau machen danke

von Stefan P (Gast)


Lesenswert?

Hallo Zusammen,
bin jetzt schon so weit beim Programieren, das ich mit der Usart Daten 
vom Hyperterminal einlesen und wieder zurück schicken kann. Jetzt möchte 
ich die Daten über den i2c- Bus auf mein Display schreiben. Dazu habe 
ich ein array angelegt  mit 5 Speicherplätzen a[5]. Nun möchte ich nur 
einen Speicherplatz a[1] ausgeben. Und da scheiterts jetzt. Folgenden 
Programmausschnitt verwende ich dazu:
1
int main(void)      
2
3
char a[5];               // Initialisierung des arrays
4
5
for(i=0;i<5;i++)        // Schreibe in Array am Anfang 1 mal gezielt 0
6
  {
7
    a[i] = 0x00;
8
  }
9
10
11
while(1)
12
{
13
   if((UCSR0A & (1<<RXC0)))   //USART Controll und Status Register 0A
14
                              // startet wenn Daten im Empfangsbuffer 
15
   {
16
    for(i=0;i<5;i++)         //Inhalt von UDR0 in a[i] schreiben (vom PC)
17
     {
18
       a[i] = UDR0;
19
       _delay_ms(6);
20
      UDR0 = a[i];          //a[i] wird an UDR0 ausgegeben (zum PC zurück) 
21
     }
22
    
23
    write_buffer_to_edip (eDip, 10, 40, a[1]);  //a[1] zum Display
24
   }
25
}

Folgende Warnmeldung bekomme ich:
1
../eDip_diagonale.c:107:4: warning: passing argument 4 of 'write_buffer_to_edip' makes pointer from integer without a cast [enabled by default]
2
../edip.h:7:22: note: expected 'char *' but argument is of type 'char'
Am Display passiert nichts.

Gebe ich bei "write_buffer_to_edip" statt a[1] den gesamten array also a 
ein so gibt er mir die gesamten 5 Zeichen aus.

: Bearbeitet durch User
von Stefan P (Gast)


Lesenswert?

Hinter "write_buffer_to_edip (eDip, 10, 40, a[1]);" ist folgendes 
Programm hinterlegt:
1
unsigned char write_buffer_to_edip (unsigned char DevAddr, unsigned int x_pos,unsigned int y_pos, char buffer[255])
2
3
{
4
  unsigned int buf_len, j, k, sum;
5
  unsigned char edip_buf[50], summe;
6
  char buf[50], byte_summe; 
7
8
  edip_buf[0] = DC1;       //Text Font einstellen
9
  edip_buf[2] = ESC;
10
  edip_buf[3] = Z;
11
  edip_buf[4] = F;
12
  edip_buf[5] = 5;  //Font 5 //Zeichenkette senden
13
  edip_buf[6] = ESC;
14
  edip_buf[7] = Z;
15
  edip_buf[8] = L;  //L - linksbündig
16
  edip_buf[9] = x_pos;
17
  edip_buf[10] = y_pos;
18
  
19
  
20
 //Text in buf schreiben Bsp. "Hallo" buf = 5
21
 sprintf(buf, buffer);
22
  
23
 //buf in int buf_len schreiben
24
 buf_len = strlen(buf);
25
  
26
 //buf_len hochzählen (beginnend bei 0)
27
 for(j=0; j<buf_len; j++)
28
  {
29
    edip_buf[j+11] = buf[j];
30
  }
31
 edip_buf[1] = j+10;    // Anzahl der Nutzdaten - byte 19, 
32
 edip_buf[j+11] = 0x00;    // NULL![] fangen bei 0 an zu zählen, "=..."   fängt bei 1 an zu zählen
33
34
 sum = j+11;
35
 summe = 0;
36
 for (k=0; k<= sum; k++)
37
  {
38
    summe = summe + edip_buf[k];
39
  }
40
 edip_buf[j+12] = summe;    // Prüfbyte bcc 
41
 byte_summe = j+13;
42
 _delay_us(10);
43
44
 I2C_Write(DevAddr, edip_buf , byte_summe);
45
  
46
 return read_Ack_eDip(DevAddr);    
47
}

Alle Buchstaben sind natürlich difiniert.
Irgendwo habe ich einen Denkfehler. Habt ihr vielleicht eine Idee? Wäre 
super wenn mir hier noch mal jemand helfen könnte.
Danke schon mal im voraus

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Stefan P schrieb:
> Gebe ich bei "write_buffer_to_edip" statt a[1] den gesamten array also a
> ein so gibt er mir die gesamten 5 Zeichen aus.

Das ist ihr Job. Mit Einzelzeichen kann die Routine nicht umgehen.

von Stefan P (Gast)


Lesenswert?

Hmm das erklärt einiges.
Wie muss ich die Routine ändern, das sie funktioniert?
Oder muss ich mir was ganz anderes überlegen?

von Wolfgang (Gast)


Lesenswert?

Stefan P schrieb:
> Wie muss ich die Routine ändern, das sie funktioniert?

Warum willst du die Routine ändern?
Füttere sie einfach mit der Adresse eines \0-terminierten Char Arrays 
und guck mal in ein C-Grundlagenbuch zum Thema Stringverarbeitung in C.

von Stefan P (Gast)


Lesenswert?

oke vielen dank, das werde ich machenund mich mal versuchen

von Stefan P (Gast)


Lesenswert?

Hab mal geschaut,mit dem \0 blendet man ja eigentlich nur den rest des 
Arrays aus. Ich möchte aber nur das Byte a[1] ausgeben. Wenn ich das mit 
\0 abschließe, gibt er ja auch das Byte 0 aus.
Oder hab ich das mit den \0-terminierten Char Arrays falsch verstanden, 
bzw. etwas übersehen?

von Wolfgang (Gast)


Lesenswert?

Stefan P schrieb:
> Oder hab ich das mit den \0-terminierten Char Arrays falsch verstanden,
> bzw. etwas übersehen?

Die Funktion strlen() zählt die Zeichen, die vor der \0 im Array 
stehen. Die \0 wird bei der Übergabe an den I2C Buffer nicht mit 
übernommen. Überleg dir mal, welche Array-Elemente in der for-Schleife 
übertragen werden.

von Stefan P (Gast)


Lesenswert?

also in der for- Schleife wird mein Array gefüllt.
Nehmen wir mal an ich möchte nacheinander die Zahlen 1-5 vom PC zum µC 
übertragen. Also schreibt die for- Schleife die 1 in den a[0], die 2 in 
den a[1], usw. Ich habe dann nach einem Durchlauf der Schleife in meinem 
Array 12345 stehen. Dies kann ich auch so ausgeben. Ich möchte jetzt 
aber zB. nur die 3 auf dem Display ausgeben. Normalerweise ja mit a[2], 
geht aber ja nicht wegen meiner Routine.
Aber mit dem \0-terminieren würde er doch 123 ausgeben oder nicht? So 
hab ich es wenigstens verstanden.
Warum soll ich mit dem strlen() die Zeichen vor dem \0 zählen. Was 
bringt mir das?
Anscheinedn hab ich da gerade ein Blatt vor den Augen.

von Karl H. (kbuchegg)


Lesenswert?

Stefan P schrieb:
> also in der for- Schleife wird mein Array gefüllt.
> Nehmen wir mal an ich möchte nacheinander die Zahlen 1-5 vom PC zum µC
> übertragen. Also schreibt die for- Schleife die 1 in den a[0], die 2 in
> den a[1], usw. Ich habe dann nach einem Durchlauf der Schleife in meinem
> Array 12345 stehen.

Ja, aber nach dem bisherigen hast du nur echte Zahlen.

Um mit dieser Stringausgabe etwas ausgeben zu können, benötigst du aber 
Strings.

> Dies kann ich auch so ausgeben.

Dann sind es keine Zahen, sondern Zeichen.
Bitte gewöhn dir diese Unterschieidung an. Die ist nämlich wichtig, um 
zu verhindern, dass man ständig aneinander vorbei redet.

Da ist die Zahl 65.
Schön. Das sind zb 65 Äpfel, oder die Temperatur beträgt 65°

Dann kann man diese 65 auch als Zeichen zu einem Terminal oder einem LCD 
schicken, welches diese 65 als ASCII Code interpretiert und dafür, so 
wie es die ASCII Tabelle vorsieht, ein 'A' auf den Bildschirm malt.

Und dann gibt es noch den STring "65". Das ist eine Sequenz aus 3 
Zeichen. Dem Zeichen '6', dem Zeichen '5' und dem Zeichen '\0' welches 
jeden String abchliesst.
1
65     eine Zahl
2
'6'    ein Zeichen (mit dem ASCII Code 54)
3
'5'    noch ein Zeichen, diesmal mit dem ASCII Code 53
4
'A'    wein weiteres Zeichen, diesmal mit dem ASCII Code 65
5
"65"   ein String, bestehend aus den 3 Zeichen '6', '5' und '\0'

bitte gewöhn dir an, die unterschiedlichen Schreibweisen für Zahlen, 
Zeichen und Strings zu respektieren und richtig anzuschreiben. Sonst 
kennt sich nämlich kein Schwein (und auch nicht der Compiler) aus, was 
du meinst und was du willst.

Und PS:
Wenn du im Terminal auf die Taste 5 drückst, dann wird da nicht die Zahl 
5 übertragen, sondern das zeichen '5'. Dargestellt durch ihren ASCII 
Code. Übertragen wird also die Zahl 53, oder hexadezimal 0x35 
geschrieben, oder eben als '5' geschrieben. Unterschiedliche 
Schreibweisen, für immer dasselbe: das Bitmuster 0011 0101, welches 
entweder die Zahl 53 repräsentiert oder zb das ASCII Zeichen '5'. Welche 
Interpretation es sein soll, wird durch die Interpretation festgelegt. 
Dem Bitmuster selber sieht man es nicht an.

> Ich möchte jetzt
> aber zB. nur die 3 auf dem Display ausgeben. Normalerweise ja mit a[2],
> geht aber ja nicht wegen meiner Routine.

Richtig, weil deine Routine einen String will.
Normalerweise baut man eine String Ausgabe so auf, dass die auf eine 
Zeichenausgabe zurückgreift. Denn ein String ist ja nichts anderes als 
eine Abfolge von Zeichen. Also kann man einen String so ausgeben, dass 
man Zeichen für Zeichen ausgibt.

Allerdings ist das in deinem Fall nicht ratsam. Denn die Stringausgabe 
lässt sich nicht sinnvoll einfach auf eine EInzelzeichenausgabe 
runterbrechen. Dazu ist der Aufwand zu gross.
Aber man kann die Umkehrung machen. Man kann eine Funktion schreiben, 
die ein einzelnes Zeichen ausgibt, indem sie es in einem String verpackt 
und dann den String ausgibt.
1
void unsigned char write_character_to_edip (unsigned char DevAddr, unsigned int x_pos,unsigned int y_pos, char c)
2
{
3
  char buffer[2];
4
5
  buffer[0] = c;
6
  buffer[1] = '\0';
7
8
  write_buffer_to_edip( DevAddr, x_pos, y_pos, buffer );
9
}

gewöhn dich daran.
nicht blind irgendwelche Dinge umschreiben, sondern erst mal überlegen, 
wie man mit den bereits vorhandenen Routinen das gewünschte 
bewerkstelligen kann, indem man die Argumente geeignet verpackt und dann 
die bereits bestehenden Funktionen hernimmt.


Dasselbe gilt zb auch für eine Funktion, die einen int ausgeben soll. 
Die schreibt man auch nicht neu, sondern benutzt zb eine Funktion wie 
itoa, um aus dem int einen String zu machen, und gibt dann den String 
aus.

: Bearbeitet durch User
von Stefan P (Gast)


Lesenswert?

Hallo Karl,
oh ja Sorry da hast du recht da werde ich in Zukunft dran denken.

Und vielen Dank für deine ausführliche Antwort. Jetzt hab ich es 
hinbekommen. Hab trauriger weise den ganzen Tag dran gesessen und 
irgendwie immer in die falsche Richtung gedacht.
Jetzt muss ich nur noch meine Nutzdaten aus den Zeichenketten 
herausfiltern, aber das sollte ich jetzt hoffentlich alleine 
hinbekommen.
Danek nochmals

von Wolfgang (Gast)


Lesenswert?

Stefan P schrieb:
> Jetzt muss ich nur noch meine Nutzdaten aus den Zeichenketten
> herausfiltern

Denk dran, dass in den NMEA-Sentences die Koordinatenangaben, die wie 
Dezimalzahlen aussehen, keine Dezimalzahlen sind ;-)

von Stefan P (Gast)


Lesenswert?

die Koordinatendaten werden als ASCII von der GPS- Maus gesendet, 
getrennt von Komma. Jetzt hatte ich vor die Komma zu zählen und die 
jeweiligen Daten so heraus zu filtern. Das es keine Dezimalzahlen sind 
weiß ich, aber das ist auch gut so, da mein Display ja eh den ASCII- 
Code benötigt und ihn dann intern umwandelt :)

von hans (Gast)


Lesenswert?

und?

von Karl H. (kbuchegg)


Lesenswert?

na wenn er es bis jetzt noch nicht fertig hat, dann soll er sich sein 
Lehrgeld zurückgeben lassen.
Auf einer UART Zeichen beginnend bei einem '$' bis zum '\n' einzulesen, 
den String an ',' in Einzelteile zu zerlegen und die "Texte" bei Bedarf 
in echte Zahlen umzuwandeln ist jetzt nicht gerade Raketentechnik.

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.