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


von Frank M. (frank85)


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:
1
 char uart_rd;
2
char *zeigerZeichen = &uart_rd;
3
char string[200];
4
unsigned short i = 0;
5
6
void main() {
7
8
  ADCON1 |= 0x0F;                 // Configure AN pins as digital
9
  CMCON  |= 7;                    // Disable comparators
10
  TRISB = 0;
11
  PORTB = 0;
12
 
13
 
14
  UART1_Init(4800);              // Initialize UART module at 4800 bps
15
  Delay_ms(100);                  // Wait for UART module to stabilize
16
17
18
  while (1) {                     // Endless loop
19
                  if (RCREG1 == '$') {            //Prüft ob $-Zeichen im Register
20
                     do{
21
                            if (PIR1.RC1IF == 1) { //Buffer Inerruptflag
22
                                string[i] = RCREG1;
23
                                ++i;
24
                         }
25
                     }while(RCREG1 != '*');
26
                      break;
27
                 }
28
29
30
       }
31
uart_rd = string[1];
32
PORTB = uart_rd;
33
asm sleep;
34
}

von holger (Gast)


Lesenswert?

++i;

Wo wird das wieder auf 0 gesetzt?

von holger (Gast)


Lesenswert?

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

Alles auf Reset;)

Vieleicht wird so ja ein Schuh draus:
1
char uart_rd;
2
char *zeigerZeichen = &uart_rd;
3
char string[200];
4
unsigned short i = 0;
5
unsigned char scratch;
6
7
void main() {
8
9
  ADCON1 |= 0x0F;                 // Configure AN pins as digital
10
  CMCON  |= 7;                    // Disable comparators
11
  TRISB = 0;
12
  PORTB = 0;
13
 
14
 
15
  UART1_Init(4800);              // Initialize UART module at 4800 bps
16
  Delay_ms(100);                  // Wait for UART module to stabilize
17
18
  scratch = RCREG1; // reset PIR1.RC1IF
19
20
  while (1)
21
  {                     // Endless loop
22
 
23
    while(PIR1.RC1IF == 0); // wait for char
24
    scratch = RCREG1;       // read receive register and reset PIR1.RC1IF
25
26
    if (scratch == '$')
27
    {
28
      string[i] = scratch;
29
      i++;
30
      
31
      do
32
      {
33
       while(PIR1.RC1IF == 0); // wait for char
34
       scratch = RCREG1;       // read receive register and reset PIR1.RC1IF
35
       string[i] = scratch;
36
       i++;
37
      }while(scratch != '*');
38
      
39
      break; // while(1)
40
    }  
41
   
42
  }
43
uart_rd = string[1];
44
PORTB = uart_rd;
45
asm sleep;
46
}

von Frank (Gast)


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?

von Frank (Gast)


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

von Frank M. (frank85)


Lesenswert?

Hab vergessen mich einzuloggen, darum "GAST", sorry.

von Karl H. (kbuchegg)


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?

von Karl H. (kbuchegg)


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.

von Frank M. (frank85)


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).
1
$GPVTG,,T,,M,,N,,K*4E
2
$GPGGA,003732.861,,,,,0,00,50.0,,M,,,,0000*24
3
$GPRMC,003732.861,V,,,,,,,020504,,*26
4
$GPVTG,,T,,M,,N,,K*4E
5
$GPGGA,003733.861,,,,,0,00,50.0,,M,,,,0000*25
6
$GPRMC,003733.861,V,,,,,,,020504,,*27
7
$GPVTG,,T,,M,,N,,K*4E
8
$GPGGA,003734.861,,,,,0,00,50.0,,M,,,,0000*22
9
$GPRMC,003734.861,V,,,,,,,020504,,*20
10
$GPVTG,,T,,M,,N,,K*4E
11
$GPGGA,003735.861,,,,,0,00,50.0,,M,,,,0000*23
12
$GPRMC,003735.861,V,,,,,,,020504,,*21
13
$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?
1
unsigned char uart_rd;
2
char string[200];
3
unsigned short i = 0;
4
unsigned char scratch;
5
6
void main()
7
{
8
9
 ADCON1 |= 0x0F;                 // Configure AN pins as digital
10
  CMCON |= 7;                    // Disable comparators
11
  TRISB = 0x00;
12
  PORTB = 0x00;
13
  TRISE = 0x00;
14
  PORTE = 0x00;
15
  TRISC = 0x80;
16
  PORTC = 0x02;
17
18
19
  UART1_Init(4800);              // Initialize UART module at 4800 bps
20
  Delay_ms(100);                  // Wait for UART module to stabilize
21
22
 while(1)
23
 {
24
 
25
  scratch = RCREG1; // reset PIR1.RC1IF
26
  
27
28
  while (1)
29
  {                     // Endless loop
30
    while(PIR1.RC1IF == 0); // wait for char
31
    scratch = RCREG1;       // read receive register and reset PIR1.RC1IF
32
33
    if (scratch == '$')
34
    {
35
      while(PIR1.RC1IF == 0);
36
      scratch = RCREG1;
37
      
38
        if (scratch =='G')
39
        {
40
          while(PIR1.RC1IF == 0);
41
          scratch = RCREG1;
42
          
43
          if (scratch =='P')
44
          {
45
            while(PIR1.RC1IF == 0);
46
            scratch = RCREG1;
47
            
48
              if (scratch =='R')
49
              {
50
                while(PIR1.RC1IF == 0);
51
                scratch = RCREG1;
52
                
53
                  if (scratch =='M')
54
                  {
55
                    while(PIR1.RC1IF == 0);
56
                    scratch = RCREG1;
57
                    
58
                      if (scratch =='C')
59
                      {
60
                        while(PIR1.RC1IF == 0);
61
                        scratch = RCREG1;
62
63
                          do
64
                          {
65
                            while(PIR1.RC1IF == 0); // wait for char
66
                            scratch = RCREG1;       
67
                            string[i] = scratch;
68
                            ++i;
69
                           }while(scratch != '*');
70
71
                            break; // while(1)
72
                       }
73
                   }
74
               }
75
           }
76
        }
77
    }
78
79
  }
80
  uart_rd = string[2];
81
  PORTB = uart_rd;
82
  PORTE = RCSTA1;
83
 }
84
}

Danke.

von Karl H. (kbuchegg)


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
1
void main()
2
{
3
4
 ADCON1 |= 0x0F;                 // Configure AN pins as digital
5
  CMCON |= 7;                    // Disable comparators
6
  TRISB = 0x00;
7
  PORTB = 0x00;
8
  TRISE = 0x00;
9
  PORTE = 0x00;
10
  TRISC = 0x80;
11
  PORTC = 0x02;
12
13
14
  UART1_Init(4800);              // Initialize UART module at 4800 bps
15
  Delay_ms(100);                  // Wait for UART module to stabilize
16
17
 while(1)
18
 {
19
   receiveGPSString();
20
21
   uart_rd = string[1];
22
   PORTB = uart_rd;
23
 }
24
}

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
1
char waitForChar()
2
{
3
  while(PIR1.RC1IF == 0)
4
    ;
5
  return RCREG1;
6
}

abstrahiert schon mal einiges an Komplexität von den verwendenden 
Stellen weg, indem dort dann einfach nur noch steht
1
    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.

von Frank M. (frank85)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


Lesenswert?

1
char waitForChar()
2
{
3
  while(PIR1.RC1IF == 0)
4
    ;
5
  return RCREG1;
6
}
7
8
void receiveGPSString()
9
{
10
  char c;
11
12
  // auf den Satzanfang warten, GPS Sätze beginnen immer mit $
13
  c = waitForChar();
14
  while( c != '$' )
15
    c = waitForChar();
16
17
  // hab ihn, jetzt den Satz lesen bis zum '*'
18
  c = waitForChar();
19
  i = 0;
20
  while( c != '*' && i < sizeof(string) - 1 ) {
21
    string[i++] = c;
22
    c = waitForChar();
23
  }
24
25
  // sicher ist sicher. wir wollen einen schoenen C String haben
26
  string[i] = '\0'; 
27
}

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

von Frank M. (frank85)


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?

von Frank M. (frank85)


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.
1
while( c != '*' && i < sizeof(string) - 1 ) {
2
    string[i++] = c;
3
    c = waitForChar();
4
  }
5
6
  // sicher ist sicher. wir wollen einen schoenen C String haben
7
  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.

von Frank M. (frank85)


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.

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.