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
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.
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
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
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.
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
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.
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.
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
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
intmain(void)
2
3
chara[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.
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
returnread_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
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.
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.
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?
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.
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.
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.
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.
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
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 ;-)
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 :)
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.