www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik NMEA von GPS-Modul mit PIC18f8520 auswerten, klappt nicht richtig.


Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein GPS-Modul welches brav und schön sauber Daten an das 
Hyperterminal sendet.
Diese Daten möchte ich mit meinem PIC auswerten.
Habe zuerst einmal ein ganz simples Programm geschrieben, welches Daten 
ab dem $-Zeichen in einen String kopiert, bis zu dem *-Zeichen.

Das Programm funktioniert im Debugger, sprich alle vorgegebenen Zeichen 
im RCREG1-Register werden in den String, Register für Register 
beschrieben.
Bei dem *-Zeichen wird die Schleife verlassen.

Jedoch funktioniert die Zeichenausgabe nur im Debugger.
In der Hardware wird nur der erste Buchstabe, ab zu zu mal richtig 
angezeigt (NMEA Protokoll ist das ein 'G').

Ich vermute, dass es letztendlich an der Baudrate hängt, jedoch wird ein 
Baudratenfehler von 0.17% von meinem Compiler ausgespuckt.

Bin einen kleinen Schritt weitergekommen.
Und zwar hab ich gemerkt, dass ca. ab dem String[80] die Daten stimmen.
Was hat das zu bedeuten? Kann das sein, dass das UART-Modul vom PIC ab 
da erst richtig arbeitet? Schon drei Tage versuch ichs und bin mit 
meinem Latein am Ende.
Übrigens, arbeitet der PIC mit 10Mhz, Baud 4800 und im Baud Rate 
Generator Register steht .33

Möchte noch eine Frage hinzufügen: Brauch ich unbedingt die 
Handshake-Leitungen (weiß nicht ob das Modul sie hat, hab ich aus einem 
Navi ausgebaut).

Oder hab ich einen Fehler in meinem Code:
 char uart_rd;
char *zeigerZeichen = &uart_rd;
char string[200];
unsigned short i = 0;

void main() {

  ADCON1 |= 0x0F;                 // Configure AN pins as digital
  CMCON  |= 7;                    // Disable comparators
  TRISB = 0;
  PORTB = 0;
 
 
  UART1_Init(4800);              // Initialize UART module at 4800 bps
  Delay_ms(100);                  // Wait for UART module to stabilize


  while (1) {                     // Endless loop
                  if (RCREG1 == '$') {            //Prüft ob $-Zeichen im Register
                     do{
                            if (PIR1.RC1IF == 1) { //Buffer Inerruptflag
                                string[i] = RCREG1;
                                ++i;
                         }
                     }while(RCREG1 != '*');
                      break;
                 }


       }
uart_rd = string[1];
PORTB = uart_rd;
asm sleep;
} 

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
++i;

Wo wird das wieder auf 0 gesetzt?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>++i;
>
>Wo wird das wieder auf 0 gesetzt?

Alles auf Reset;)

Vieleicht wird so ja ein Schuh draus:
char uart_rd;
char *zeigerZeichen = &uart_rd;
char string[200];
unsigned short i = 0;
unsigned char scratch;

void main() {

  ADCON1 |= 0x0F;                 // Configure AN pins as digital
  CMCON  |= 7;                    // Disable comparators
  TRISB = 0;
  PORTB = 0;
 
 
  UART1_Init(4800);              // Initialize UART module at 4800 bps
  Delay_ms(100);                  // Wait for UART module to stabilize

  scratch = RCREG1; // reset PIR1.RC1IF

  while (1)
  {                     // Endless loop
 
    while(PIR1.RC1IF == 0); // wait for char
    scratch = RCREG1;       // read receive register and reset PIR1.RC1IF

    if (scratch == '$')
    {
      string[i] = scratch;
      i++;
      
      do
      {
       while(PIR1.RC1IF == 0); // wait for char
       scratch = RCREG1;       // read receive register and reset PIR1.RC1IF
       string[i] = scratch;
       i++;
      }while(scratch != '*');
      
      break; // while(1)
    }  
   
  }
uart_rd = string[1];
PORTB = uart_rd;
asm sleep;
} 

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

danke für die Hilfe. Leider funktioniert es nicht. Plötzlich wird sogar 
garnichts in die Register geschrieben. Obwohl der Code von dir richtig 
aussieht.
Trotzdem noch was generelles. Wie war das mit dem Handshake und Pull-Ups 
oder Downs?

Autor: Frank (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab es hinbekommen, dass wieder Daten ins String-Feld geschrieben 
werden.
Jedoch verändert sich jedesmal der gespeicherte Wert im String[1].
Statt SLEEP hab ich jetzt eine Schleife eingebaut.
Noch eine Idee?

MfG Frank

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab vergessen mich einzuloggen, darum "GAST", sorry.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank schrieb:
> Ich hab es hinbekommen, dass wieder Daten ins String-Feld geschrieben
> werden.

Das sieht jetzt wie aus?

> Jedoch verändert sich jedesmal der gespeicherte Wert im String[1].

Wie ist das zu verstehen?
Wann genau verändert sich der Wert?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Edit:
Achte auch darauf, dass dein i an einer sinnvollen Stelle wieder auf 0 
zurückgestellt wird, damit der nächste GPS String auch wieder vom Anfang 
des Buffers aus abgelegt wird.

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

mit jetzt funktioniert es wieder hab ich gemeint, dass die Spannung am 
Netzteil etwas zu niedrig war.

Mit dem i zurücksetzen, hab mir da erst gar keine Gedanken gemacht. Was 
passiert wenn ich es nicht tue? i zählt bis 255 und beginnt von neu und 
verändert den String wieder von [0] an. Aber nur dann wenn sich die 
Daten an der selben Stelle im NMEA-Protokoll verändern, richtig?

Ich lass mir die Bits an 8 LEDs anzeigen (Ich hab das BIGPI4, falls 
bekannt)
und da verändern sie sich, aller ca. 3sek. Das dürfte ja theoretisch 
garnicht passieren, weil sich der String[2] z.B. garnicht ändert (sehe 
das NMEA-Prot. im Hyperterminal).
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,003732.861,,,,,0,00,50.0,,M,,,,0000*24
$GPRMC,003732.861,V,,,,,,,020504,,*26
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,003733.861,,,,,0,00,50.0,,M,,,,0000*25
$GPRMC,003733.861,V,,,,,,,020504,,*27
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,003734.861,,,,,0,00,50.0,,M,,,,0000*22
$GPRMC,003734.861,V,,,,,,,020504,,*20
$GPVTG,,T,,M,,N,,K*4E
$GPGGA,003735.861,,,,,0,00,50.0,,M,,,,0000*23
$GPRMC,003735.861,V,,,,,,,020504,,*21
$GPVTG,,T,,M,,N,,K*4E

Habe meinen Code ein bisschen geändert, möchte nur GPRMC rausfiltern.
Dann müsste mir String[2] die "0" ausgeben. Das geschieh auch, jedoch 
nur beim ersten mal und dann ändert er sich laufend.

Hier noch mein Code, geht das auch schöner?
unsigned char uart_rd;
char string[200];
unsigned short i = 0;
unsigned char scratch;

void main()
{

 ADCON1 |= 0x0F;                 // Configure AN pins as digital
  CMCON |= 7;                    // Disable comparators
  TRISB = 0x00;
  PORTB = 0x00;
  TRISE = 0x00;
  PORTE = 0x00;
  TRISC = 0x80;
  PORTC = 0x02;


  UART1_Init(4800);              // Initialize UART module at 4800 bps
  Delay_ms(100);                  // Wait for UART module to stabilize

 while(1)
 {
 
  scratch = RCREG1; // reset PIR1.RC1IF
  

  while (1)
  {                     // Endless loop
    while(PIR1.RC1IF == 0); // wait for char
    scratch = RCREG1;       // read receive register and reset PIR1.RC1IF

    if (scratch == '$')
    {
      while(PIR1.RC1IF == 0);
      scratch = RCREG1;
      
        if (scratch =='G')
        {
          while(PIR1.RC1IF == 0);
          scratch = RCREG1;
          
          if (scratch =='P')
          {
            while(PIR1.RC1IF == 0);
            scratch = RCREG1;
            
              if (scratch =='R')
              {
                while(PIR1.RC1IF == 0);
                scratch = RCREG1;
                
                  if (scratch =='M')
                  {
                    while(PIR1.RC1IF == 0);
                    scratch = RCREG1;
                    
                      if (scratch =='C')
                      {
                        while(PIR1.RC1IF == 0);
                        scratch = RCREG1;

                          do
                          {
                            while(PIR1.RC1IF == 0); // wait for char
                            scratch = RCREG1;       
                            string[i] = scratch;
                            ++i;
                           }while(scratch != '*');

                            break; // while(1)
                       }
                   }
               }
           }
        }
    }

  }
  uart_rd = string[2];
  PORTB = uart_rd;
  PORTE = RCSTA1;
 }
}

Danke.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank Müller schrieb:

> Mit dem i zurücksetzen, hab mir da erst gar keine Gedanken gemacht. Was
> passiert wenn ich es nicht tue? i zählt bis 255 und beginnt von neu und
> verändert den String wieder von [0] an. Aber nur dann wenn sich die
> Daten an der selben Stelle im NMEA-Protokoll verändern, richtig?

Da dein String Array in erster Linie nur 200 Zeichen gross ist, passiert 
dann alles mögliche

> Hier noch mein Code, geht das auch schöner?

Sicher.
Wenn du dir angewöhnen würdest nicht dem schnellen Hack 
hinterherzulaufen, sondern wenigstens halbwegs vernünftiges 
Softwaredesign betreiben würdest.
Dazu gehört auch, dass man sich Übersicht schafft, indem man 
Funktionalität in Funktionen auslagert. Dazu gehört auch, dass man sich 
die vom Programm zu leistende Arbeit in logisch zusammengehörende 
Baugruppen einteilt.

Dein Hauptprogramm sollte so aussehen
void main()
{

 ADCON1 |= 0x0F;                 // Configure AN pins as digital
  CMCON |= 7;                    // Disable comparators
  TRISB = 0x00;
  PORTB = 0x00;
  TRISE = 0x00;
  PORTE = 0x00;
  TRISC = 0x80;
  PORTC = 0x02;


  UART1_Init(4800);              // Initialize UART module at 4800 bps
  Delay_ms(100);                  // Wait for UART module to stabilize

 while(1)
 {
   receiveGPSString();

   uart_rd = string[1];
   PORTB = uart_rd;
 }
} 

Jetzt bist du am Zug die Funktion receiveGPSString zu schreiben, die 
eine komplette Zeile vom GPS einliest. Nicht mehr und nicht weniger. Sie 
soll auch nichts weiter tun, als eine komplette Zeile einzulesen - nicht 
mehr und nicht weniger.

Hinweis 2:
Eine Funktion
char waitForChar()
{
  while(PIR1.RC1IF == 0)
    ;
  return RCREG1;
}

abstrahiert schon mal einiges an Komplexität von den verwendenden 
Stellen weg, indem dort dann einfach nur noch steht
    scratch = waitForChar();

und das ist dann schon leichter zu lesen und zu verstehen als wie wenn 
da jedesmal die ganzen Details die zum einlesen eines Zeichens notwendig 
sind, aufgeführt werden.

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja hast recht.
Sollte schnell gehen und wurde dann, wie es einige auch sehen werden, 
eben schlampig.
Wenn es klappt, dann werde ich es natürlich "verschönern"
Trotzdem danke.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank Müller schrieb:
> Ja hast recht.
> Sollte schnell gehen

Genau da liegt der Trugschluss.
Mit derartigen Hacks ist man nie schneller, als wie wenn man es von 
Anfang an gleich einigermassen vernünftig macht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char waitForChar()
{
  while(PIR1.RC1IF == 0)
    ;
  return RCREG1;
}

void receiveGPSString()
{
  char c;

  // auf den Satzanfang warten, GPS Sätze beginnen immer mit $
  c = waitForChar();
  while( c != '$' )
    c = waitForChar();

  // hab ihn, jetzt den Satz lesen bis zum '*'
  c = waitForChar();
  i = 0;
  while( c != '*' && i < sizeof(string) - 1 ) {
    string[i++] = c;
    c = waitForChar();
  }

  // sicher ist sicher. wir wollen einen schoenen C String haben
  string[i] = '\0'; 
}

jetzt kannst du mal nachsehen, ob du an deinen LED ständig das 'G' 
siehst.

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zuersteinmal vielen Dank für die Mühe.

So, ich hab mal den Code eins zu eins übernommen.
Du wolltest sie doch mit ; abschließen
while( c != '$' ); , oder?

Es funktioniert einigermaßen ohne zu kritisieren.
Manchmal wenn ich den PIC resete, dann wird nichts eingelesen.
Muss ich das in der Software so schreiben, das wenn er nichts einliest, 
dass er dann das UART-Modul neue startet?

Dann noch eine Frage: Wo liegt der funktionelle Unterschied in unseren 
beiden Codes?

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, hab da was falsch verstanden.

while( c != '$' ) -> ist schon richtig. Hab mir nochmal den Code genau 
angeschaut. Bin noch nicht so fit drin.
while( c != '*' && i < sizeof(string) - 1 ) {
    string[i++] = c;
    c = waitForChar();
  }

  // sicher ist sicher. wir wollen einen schoenen C String haben
  string[i] = '\0';


Kannst du mir bitte sagen was da passiert: sizeof(string) - 1 und 
string[i] = '\0'. Weiß der Compiler dass das eine Nullterminierung vom 
kompletten String[i] ist?


Trotzdem wo liegt der Unterschied zwischen deinem und meinem Code (klar 
professioneller geschieben)?
Ich muss wissen wo mein Denkfehler ist.

Autor: Frank Müller (frank85)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sizeof(string) - 1 -> verstehe ich jetzt.

Ich freu mich, dass es funktioniert. Wär nur schöner gewesen, wenn es 
auf meinem Misst gewachsen wäre :-)

Trotzdem wo liegt der Unterschied zwischen deinem und meinem Code (klar
professioneller geschieben)?
Ich muss wissen wo mein Denkfehler ist.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.