NMEA von GPS-Modul mit PIC18f8520 auswerten, klappt nicht richtig.

OP #1798706
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
}
Gast #1798774
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
}
Gast #1799863
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?
Gast #1799960
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
OP #1800002
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.
#1800009
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.
#1800031
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.
OP #1800081
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?
OP #1800107
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.
OP #1800129
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren