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
Entweder ist dein " Auftraggeber" verstrahlt, oder du bist völlig fehl am Platze. Welche Schule ist das ?
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.
:
Bearbeitet durch User
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
-Ansteuerung UART nach Datenblatt des Mega644. -Zerscheibeln und interpretieren der Daten nach NMEA ( frag gurggell )
achso oke dann werd ich mich da mal schlau machen danke
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
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
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.
Hmm das erklärt einiges. Wie muss ich die Routine ändern, das sie funktioniert? Oder muss ich mir was ganz anderes überlegen?
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.
oke vielen dank, das werde ich machenund mich mal versuchen
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.
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
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.
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.