czwartek, 12 kwietnia 2018

[22] STM32F7 - Moduł GPS FGPMMOP6

Ten post chciałbym poświęcić na opisanie sposobu odczytu danych z modułu GPS FPGMMOP6. Dane po odczycie będą wyświetlane na wyświetlaczu zamontowanym w zestawie Discovery.

[Źródło: http://www.st.com/en/evaluation-tools/32f746gdiscovery.html]


Moduł GPS:



Układ komunikuje się poprzez interfejs UART. Układ posiada następujące wyprowadzenia:

  • VCC - zasilanie modułu 3.3V do 5V
  • EN - aktywacja odbiornika. Na tą nóżkę należy podać stan wysoki.
  • GND - masa układu.
  • V_BACK - zasilanie zapasowe. Tutaj można zasilać układ np. z baterii. Napięcie powinno wynosić 3.3V
  • 3D-FIX - sygnalizacja połączenia z 3D-fix
  • 1PPS - wyjście sygnału 1Hz
  • GND - masa układu
  • TX - transmisja danych od modułu do mikrokontrolera.
  • RX - odbieranie danych od mikrokontrolera.

Podłączenie układu jest następujące:

  • VCC - 3.3V;
  • EN - GPIOx;
  • GND -  GND;
  • V_BACK - ja podłączyłem do zasilania bateryjnego 3.3V;
  • 3D-FIX - do diody sygnalizacyjnej lub mikrokontrolera do uzyskania informacji;
  • 1PPS - do diody sygnalizacyjnej lub mikrokontrolera do uzyskania informacji
  • GND - GND
  • TX - pin RX
  • RX - pin TX

Pin EN może zostać niepodłączony, podobnie jak V_BACK.

Przed rozpoczęciem pracy z tym układem należy pamiętać, że może nie udać się uruchomić układu w budynku. Może wystąpić problem z ustaleniem pozycji. W takim wypadku układ należy wystawić za okno na na chwilę, aż do uzyskania wyników. Taki sposób może rozwiązać sprawę tylko na chwilę, i może zajść potrzeba ciągłego utrzymywania układu za oknem w celu uzyskania ciągłych odczytów.

Poniżej przedstawiłem szybki program na Arduino, który posłuży jako symulacja otrzymywanych pakietów. Dzięki temu można pisać i testować aplikację bez potrzebny trzymania czujnika za oknem.

Istnieje możliwość edycji pakietów przesyłanych przez GPS np. za pomocą programu MiniGps 4.1. 


Zwracane Dane:



Układ na magistralę UART będzie zwracał komunikaty w standardzie NMEA. Każda z przesłanych informacji rozpoczyna się od znaku "$" po nim przychodzi nazwa pakietu informacji po którym podawane są dane w odpowiedniej kolejności. Struktura informacji dla odpowiedniej ramki wygląda następująco:

$GPGGA - Global Positioning System Fix Data (czas i pozycja).

$GPGGA,Data1,Data2,Data3,Data4,Data5,Data6,Data7,Data8,Data9,Data10,Data11

Przykładowa ramka:

$GPGGA,181455.000,5022.3220,N,01850.9486,E,1,4,20.73,337.7,M,42.1,M,,*66

Zdekodowanie danych:

Identyfikator ramki - $GPGGA
Data 1 - hhmmss.sss- Czas w formacie UTC
Data 2 - ddmm.mmmm-  Szerokość geograficzna - ddmm.mmmm. Ostatnia litera oznacza dla N północ nastomiast S to jest południe.
Data 3 - ddmm.mmmm - Długość geograficzna
Data 4 - Jakość - Możliwe są dane 0 (błędne), 1 (GPS fix) oraz (DGPS fix)
Data 5 - Liczba satelitów z których odczytano dane
Data 6 - Względna dokładność pozycji poziomej
Data 7 - Wysokość
Data 8 - Wysokość geoidy powyżej elipsoidy WGS84
Data 9 - Czas od ostatnie aktualizacji DGPS
Data 10 - Stacja referencyjna DGPS
Data 11 - Suma kontrolna

$GPGSA - GPS DOP and active satellites (tryb pracy odbiornika, aktywne satelity).

$GPGSA,Data1,Data2,Data3,Data4,Data5,Data6

Przykładowa ramka:

$GPGSA,A,3,15,21,18,22,,,,,,,,,20.76,20.73,0.97*07

Zdekodowanie danych:

Identyfikator ramki - $GPGSA
Data 1 - Tryb pracy do wyboru ręczny M bądź automatyczny A
Data 2 - Jakość - Możliwe są dane 0 (błędne), 1 (GPS fix) oraz (DGPS fix)
Data 3 - Satelity jakie biorą udział przy obliczaniu pozycji. Wyświetlane jest jedenaście wyników, gdy układ korzystał z mniejszej ilości to pozostałe pola zostają puste.
Data 4 - PDOP czyli rozmycie pozycji
Data 5 - HDOP czyli rozmycie poziome
Data 6 - VDOP czyli rozmycie pionowe 
Data 7 - Suma kontrolna

$GPGSV - GPS Satellites in view (ilość widocznych satelit, ich numery identyfikacyjne)

Zdekodowanie danych:

$GPGSV,Data1,Data2,Data3,Data4,Data5,Data6,Data7,Data8,Data9,Data10,Data11

Identyfikator ramki - $GPGSV
Data 1 - Ilość dostępnych wiadomości
Data 2 - Numer wiadomości. Przesyłane są kolejno trzy wiadomości, numerowane od 1.
Data 3 - Liczba widocznych satelitów
Data 4 - Identyfikator satelity 
Data 5 - Wysokość podawana w stopniach
Data 6 - Stopnie od północy
Data 7 - Siła sygnału podawana w decybelach 
Data 8 - Data 10 - Dane o kolejnych satelitach. W jednej wiadomości mogą przyjść informacji o maksymalnie czterech satelitach. Ilość otrzymanych wiadomości zależy od ilości widocznych satelit.
Data 11 - Suma kontrolna 

$GPRMV - Recommended minimum specific GPS/Transmit data (czas, data, pozycja, kierunek oraz prędkość)

Zdekodowanie danych:

$GPRMV,Data1,Data2,Data3,Data4,Data5,Data6,Data7,Data8,Data9,Data10

Identyfikator ramki - $GPRMV
Data 1 - Czas w formacie UTC
Data 2 - Poprawność danych. Znacznik A dane wporządku, znacznik V dane błędne
Data 3 - Szerokość geograficzna. Po danych podawany jest znak kierunku geogr. N - północ, S - południe.
Data 4 - Wysokość geograficzna. Po danych podawany jest znak kierunku geogr. E - wschód, W - zachód.
Data 5 - Prędkość w węzłach
Data 6 - Kurs
Data 7 - Data w formacie ddmmyy
Data 8 - Deklinacja magnetyczna E - wschód, W - zachód
Data 9 - Tryb pracy: A - autonomiczny, D - dyferencyjny, E - estymowany. 
Data 10 - Suma kontrolna

$GPVTG - Track Made Good and Ground Speed (kierunek i prędkość)

Zdekodowanie danych:

$GPVTG,Data1,Data2,Data3,Data4,Data5,Data6,Data7,Data8,Data9,Data10

Identyfikator ramki - $GPVTG
Data 1 - Prawdziwy kurs (True track)
Data 2 - Poprawność danych. Znacznik T oznacza dane poprawne dla parametru 1.
Data 3 - Nie wykorzystywany parametr
Data 4 - Poprawność danych. Znacznik M (Magnetic track)
Data 5 - Prędkość w węzłach
Data 6 - Oznaczenie prędkości w węzłach N
Data 7 - Prędkość w km/h
Data 8 - Oznaczenie K, czyli wcześniejsza dana km/h
Data 9 Tryb pracy: A - autonomiczny, D - dyferencyjny, E - estymowany. 
Data 10 - Suma kontrolna

Po podłączeniu zasilania dane będą wywalane hurtem, wobec tego naszym zadaniem jest obrobienie i przedstawienie otrzymanych danych. 


Test na Arduino:



Poniżej przedstawię krótki program na Arduino służący mi do odbioru danych z układu:

  1. #include <SoftwareSerial.h>
  2. SoftwareSerial GPS(23); // RX, TX
  3. void setup()  
  4. {
  5.   Serial.begin(115200);
  6.   while (!Serial) { }
  7.   softSerialGps.begin(9600);
  8. }
  9. void loop()
  10. {
  11.   if (softSerialGps.available())
  12.   {
  13.     Serial.write(softSerialGps.read());
  14.   }
  15. }

Komunikacja z GPS odbywa się z prędkością 9600 natomiast komunikacja z komputerem została ustawiona na 115200.

Wykorzystując program powyżej otrzyma się bezpośredni zrzut z danych nie obrobionych. Można też wykorzystać bibliotekę TinyGps++, która obrobi te dane w bardziej czytelnym formacie.

Po podłączeniu i złapaniu połączenia z satelitami układ zwraca następujące informacje:


Dane z modułem wyciągniętym za okno w celu uzyskania informacji o pozycji.



Jak widać otrzymane dane pozwalają na zdekodowanie pakietów GPGGA, GPGSA, GPRMC, GPVTG.

Do testów można wykonać sobie program na Arduino który będzie przesyłał dane w odpowiednim formacie.

  1. /* GPS test aplication */
  2. #include <SoftwareSerial.h>
  3.      
  4. SoftwareSerial GPS(2, 3); // RX, TX
  5. char line1[] = "$GPGGA,160249.000,5004.4736,N,01954.6987,E,1,5,1,91,257.3,M,42.0,M,,*53\r\n";
  6. char line2[] = "$GPGSA,A,3,31,02,12,05,25,,,,,,,,2.12,1.92,0.92*00\r\n";
  7. char line3[] = "$GPRMC,160250.000,A,5004.4736,N,01954.7006,E,0.56,80.45,020418,,,A*54\r\n";
  8. char line4[] = "$GPVTG,80.45,T,,M,0.56,N,1.03,K,A*05\r\n";
  9.    
  10. void setup()  
  11. {
  12.   Serial.begin(115200);
  13.      
  14.   while (!Serial) { }
  15.   GPS.begin(9600);
  16. }
  17.      
  18. void loop()
  19. {
  20.   GPS.write(line1);
  21.   GPS.write(line2);
  22.   GPS.write(line3);
  23.   GPS.write(line4);
  24.   Serial.write(line1);
  25.   Serial.write(line2);
  26.   Serial.write(line3);
  27.   Serial.write(line4);
  28.   delay(2000);
  29. }

Powyższy program przesyłane wysyłane pakiety jako GPS z prędkością 9600 oraz na komputer z prędkością 115200. Pakiety danych są wprowadzone na sztywno.

Drugi program testowy ze zmieniającymi się parametrami. Wprowadziłem kilka rodzajów ramek które wgrywam cyklicznie jedna po drugiej:

  1. char line1[] = "$GPGGA,160249.000,5004.4736,N,01954.6987,E,1,5,1.91,257.3,M,42.0,M,,*53\r\n";
  2. char line1_1[] = "$GPGGA,082334.000,4124.8963,S,08151.6838,W,0,4,20.73,337.7,M,23.0,M,,*53\r\n";
  3. char line1_2[] = "$GPGGA,120032.000,2378.1279,N,06523.5732,E,2,8,8.91,237.7,M,78.0,M,,*53\r\n";
  4. char line1_3[] = "$GPGGA,190061.000,8721.7519,S,09856.5672,W,1,3,10.42,123.7,M,34.0,M,,*53\r\n";
  5. char line1_4[] = "$GPGGA,023296.000,5004.2347,N,04242.6934,E,2,6,2.91,045.7,M,11.0,M,,*53\r\n";
  6. char line2[] = "$GPGSA,A,3,31,02,12,05,25,,,,,,,,2.12,12.92,1,23*00\r\n";
  7. char line2_1[] = "$GPGSA,M,1,31,02,12,05,25,,,,,,,,12.12,10.92,5.34*00\r\n";
  8. char line2_2[] = "$GPGSA,A,2,31,02,12,05,25,,,,,,,,23.12,11.92,10.12*00\r\n";
  9. char line2_3[] = "$GPGSA,M,3,31,02,12,05,25,,,,,,,,52.12,12.92,2.56*00\r\n";
  10. char line2_4[] = "$GPGSA,A,1,31,02,12,05,25,,,,,,,,12.12,23.92,45.11*00\r\n";
  11. char line3[] = "$GPRMC,160250.000,A,5004.4736,N,01954.6987,E,0.56,80.45,020418,,,A*54\r\n";
  12. char line3_1[] = "$GPRMC,082334.000,V,4124.8963,N,08151.6838,W,1.56,30.25,050118,,,A*54\r\n";
  13. char line3_2[] = "$GPRMC,120032.000,A,2378.1279,N,06523.5732,E,5.56,45.75,150918,,,A*54\r\n";
  14. char line3_3[] = "$GPRMC,190059.000,V,8721.7519,N,09856.5672,W,2.56,21.25,221118,,,A*54\r\n";
  15. char line3_4[] = "$GPRMC,023296.000,A,5004.2347,N,04242.6934,E,10.56,54.85,300418,,,A*54\r\n";
  16. char line4[] = "$GPVTG,80.45,T,,M,2.56,N,1.03,K,A*05\r\n";
  17. char line4_1[] = "$GPVTG,40.45,T,,M,34.56,N,10.53,K,A*05\r\n";
  18. char line4_2[] = "$GPVTG,20.45,T,,M,12.56,N,23.78,K,A*05\r\n";
  19. char line4_3[] = "$GPVTG,83.45,T,,M,23.56,N,09.23,K,A*05\r\n";
  20. char line4_4[] = "$GPVTG,75.45,T,,M,19.56,N,26.84,K,A*05\r\n";



Programowanie:



Aplikacja będzie obsługiwała układ GPS, dwa UART-y oraz wyświetlacz umieszczony na płytce Discovery.

Jeden z UART-ów będzie pobierał dane z modułu GPS, drugi będzie przesyłał przerobione dane do komputera. Wyświetlacz natomiast będzie wyświetlał obrobione dane.

Podstawowy problem który należy odpowiednio rozwiązać to duża ilość danych jaka będzie odbierana po UARCIE w dosyć szybkim tempie.

Ja wykorzystałem następujący mechanizm. Najpierw odbieram dane dla pierwszej serii, po czym wyłączam przerwania dla UARTu. Następnie kopiuje dane do bufora pomocniczego i na nich wykonuje operacje. Myślałem jeszcze nad takim rozwiązaniem, że po skopiowaniu danych dalej je odbieram, a w międzyczasie wykonuje pozostałe operacje. Natomiast informacje z modułu GPS są wysyłane dosyć często więc operacje na nich musiałbym wykonywać w przerwie po odebraniu pierwszej paczki a odbiorem drugiej. Ten czas wydaje mi się za krótki na ich obrobienie i wyświetlanie na wyświetlaczu. W związku z tym zostałem przy opcji 1.

Obsługa przerwania od UARTA wraz z funkcjami pomocniczymi:

  1. #define DISPL_UART6_ON_UART1
  2. /*
  3.  * @brief: function use for checking receive parameter
  4.  *          it is use for checking if 4 lines of data has been receive
  5.  * @param: receiveData: receive byte of information throw uart
  6.  * @ret:   none        
  7.  * */
  8. void checkDataReceive(uint8_t receiveData)
  9. {
  10.     if(receiveData == 0x0D && receiveWholeLinesCounts_USART6 < 4)
  11.     {
  12.         if(strstr((char*)USART6_Buffer, "$GPGGA") != NULL)
  13.         {
  14.             receiveWholeLinesCounts_USART6++;
  15.         }
  16.     }
  17. }
  18. /*
  19.  * @brief: function use for checking if receive buffer is full
  20.  * @param: none
  21.  * @ret:   none        
  22.  * */
  23. void checkIfBufferFullThenClear(void)
  24. {
  25.     if(receiveBufferCount_USART6 == (Buffer_Size - 1))
  26.     {
  27.         receiveBufferCount_USART6 = 0;
  28.         receiveWholeLinesCounts_USART6 = 0;
  29.         memset(USART6_Buffer, 0x00, sizeof(Buffer_Size));
  30.     }
  31. }
  32. //--------------------------------------------------------------
  33. #if USART6_USE==1
  34. void USART6_IRQHandler(void)
  35. {
  36.     static uint8_t dane;
  37.     //Sprawdzenie czy przerwanie wywolane przez odebranie danych
  38.     if (USART6->ISR & USART_ISR_RXNE)
  39.     {
  40.         dane = ((USART6)->RDR);
  41.         *(USART6_Buffer + receiveBufferCount_USART6) = dane;
  42.         receiveBufferCount_USART6++;
  43.         #ifdef DISPL_UART6_ON_UART1
  44.         usart_SendByte(USART1, dane);
  45.         #endif
  46.         checkDataReceive(dane);
  47.         checkIfBufferFullThenClear();
  48.     }
  49.     USART_INT_ClearAllFlags(USART6, USART6_IRQn);
  50. }
  51. #endif

Dodatkowo pod płytkę można podłączyć trzy piny EN, 3D-Fix oraz PPS. Pierwszy uruchamia układ GPS. W wykorzystywanym prze zemnie module można go pominąć. Następnie 3D-Fix, który informuje gdy udało się określić pozycję. Ostatni pin PPS jest wyjście sygnału 1Hz. Informuje on o działaniu modułu GPS.

Dane przechowywane są w kilku strukturach:

  1. /* == struct for GPGGA - Global Positioning System Fix Data == */
  2. typedef struct{
  3.     gps_Time_TypeDef ReadTime;              /* UTC time */
  4.     float Latitude;                         /* Latitude */
  5.     gps_Directions LatitudeDirection;       /* N - north; S - south*/
  6.     float Longitude;                        /* Longitude */
  7.     gps_Directions LongitudeDirection;      /* E - east; W - west*/
  8.     gps_FixValue FixValue;                  /* 0 - Error; 1 - GPS Fix; 2 - DGPS Fix. */
  9.     uint8_t Satellites;                     /* Number of satellites*/
  10.     float HdopValue;                        /* HDOP */
  11.     float Altitude;                         /* Altitude above the sea */
  12.     float GeoidAboveWGS84;                  /* Height of geoid above WGS84 ellipsoid */
  13. }gps_GPGGA_TypeDef;
  14. /* == struct for GPGSA - GPS DOP and active satellites == */
  15. typedef struct{
  16.     uint8_t WorkMode;       /* A - automatic; M - manual */
  17.     uint8_t FixValue;       /* 0 - Error; 1 - GPS Fix; 2 - DGPS Fix. */
  18.     uint8_t Satelites[12];  /* Number of satellites use for calculate position */
  19.     float PdopValue;        /* PDOP */
  20.     float HdopValue;        /* HDOP */
  21.     float VdopValue;        /* VDOP */
  22. }gps_GPGSA_TypeDef;
  23. /* == struct for GPRMC - Recommended minimum specific GPS/Transit data == */
  24. typedef struct{
  25.     gps_Time_TypeDef ReadTime;              /* UTC time */
  26.     uint8_t DataValidInfo;                  /* A - OK, V - error */
  27.     float Latitude;                         /* Latitude */
  28.     gps_Directions LatitudeDirection;       /* N - north; S - south*/
  29.     float Longitude;                        /* Longitude */
  30.     gps_Directions LongitudeDirection;      /* E - east; W - west*/
  31.     float SpeedInKnots;                     /* Speed in knots */
  32.     float RealCourse;                       /* In radious */
  33.     gps_Date_TypeDef ReadDate;              /* Date from gps */
  34.     float Direction;                        /* Course */
  35.     uint8_t WorkMode;                       /* A - autonomic, D - dyference, E - esymowany */
  36. }gps_GPRMC_TypeDef;
  37. /* struct for GPVTG - Track Made Good and Ground Speed */
  38. typedef struct{
  39.     float RealCourse;                       /* In radious */
  40.     uint8_t DataValidInfo_1;                /* T - true track made good */
  41.     uint8_t DataValidInfo_2;                /* T - true track made good */
  42.     float SpeedInKnots;                     /* Speed in knots */
  43.     float SpeedInKm;                        /* Speed in km/h */
  44.     uint8_t WorkMode;                       /* A - autonomic, D - dyference, E - esymowany */
  45. }gps_GPVTG_TypeDef;

Aktywacja obrabiania poszczególnych pakietów wykonuje się poprzez ustawienie flagi w strukturze:

  1. typedef struct{
  2. #if GPS_GPGGA_USE == 1
  3.     uint8_t Gps_Gpgga_Flag_ReadTime:1;
  4.     uint8_t Gps_Gpgga_Flag_Latitude:1;
  5.     uint8_t Gps_Gpgga_Flag_LatitudeDirection:1;
  6.     uint8_t Gps_Gpgga_Flag_Longitude:1;
  7.     uint8_t Gps_Gpgga_Flag_LongitudeDirection:1;
  8.     uint8_t Gps_Gpgga_Flag_FixValue:1;
  9.     uint8_t Gps_Gpgga_Flag_Satellites:1;
  10.     uint8_t Gps_Gpgga_Flag_HdopValue:1;
  11.     uint8_t Gps_Gpgga_Flag_Altitude:1;
  12.     uint8_t Gps_Gpgga_Flag_GeoidAboveWGS84:1;
  13. #endif
  14. #if GPS_GPGSA_USE == 1
  15.     uint8_t Gps_Gpgsa_Flag_WorkMode:1;
  16.     uint8_t Gps_Gpgsa_Flag_FixValue:1;
  17.     uint8_t Gps_Gpgsa_Flag_Satelites:1;
  18.     uint8_t Gps_Gpgsa_Flag_PdopValue:1;
  19.     uint8_t Gps_Gpgsa_Flag_HdopValue:1;
  20.     uint8_t Gps_Gpgsa_Flag_VdopValue:1;
  21. #endif
  22. #if GPS_GPRMC_USE == 1
  23.     uint8_t Gps_Gprmc_Flag_ReadTime:1;
  24.     uint8_t Gps_Gprmc_Flag_DataValidInfo:1;
  25.     uint8_t Gps_Gprmc_Flag_Latitude:1;
  26.     uint8_t Gps_Gprmc_Flag_LatitudeDirection:1;
  27.     uint8_t Gps_Gprmc_Flag_Longitude:1;
  28.     uint8_t Gps_Gprmc_Flag_LongitudeDirection:1;
  29.     uint8_t Gps_Gprmc_Flag_Speed:1;
  30.     uint8_t Gps_Gprmc_Flag_RealCourse:1;
  31.     uint8_t Gps_Gprmc_Flag_ReadDate:1;
  32.     uint8_t Gps_Gprmc_Flag_Direction:1;
  33.     uint8_t Gps_Gprmc_Flag_WorkMode:1;
  34. #endif
  35. #if GPS_GPVTG_USE == 1
  36.     uint8_t Gps_Gpvtg_Flag_RealCourse:1;
  37.     uint8_t Gps_Gpvtg_Flag_DataValidInfo_1:1;
  38.     uint8_t Gps_Gpvtg_Flag_DataValidInfo_2:1;
  39.     uint8_t Gps_Gpvtg_Flag_Speed:1;
  40.     uint8_t Gps_Gpvtg_Flag_SpeedInKm:1;
  41.     uint8_t Gps_Gpvtg_Flag_WorkMode:1;
  42. #endif
  43. }gps_enableFlags_TypeDef;

Dla każdej flagi przewidziałem jeden bit informujący o włączeniu (1) bądź wyłączeniu (0) obrabiania określonych danych.

Teraz krótko przedstawię funkcje obrabiające dane:

W pętli while znajduje się wywołanie funkcji sprawdzającej czy udało się otrzymać pełną ramkę danych od układu:

  1. GPS_DataStatus_TypeDef GPS_CheckDataAvaliable(void)
  2. {
  3.     char gpsBuffer[Buffer_Size] = {0x00};
  4.     GPS_DataStatus_TypeDef operationStatus = GPS_Result_DataRead;
  5.     uint8_t frameToSearch[5] = {'G', 'P', 'G', 'G', 'A'};
  6.     char *pointerToData;
  7.     if(receiveWholeLinesCounts_USART6 == 4)
  8.     {
  9.        memcpy(gpsBuffer, USART6_Buffer, receiveBufferCount_USART6);
  10.        memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
  11.        volatile uint8_t positionStart = gps_SearchFrameForBuffer(gpsBuffer, frameToSearch,
  12.                sizeof(frameToSearch)/sizeof(frameToSearch[0]), receiveBufferCount_USART6);
  13.        if(positionStart == 0)                                   /* Error no frame in buffer */
  14.        {
  15.            receiveWholeLinesCounts_USART6 = 0;
  16.            receiveBufferCount_USART6 = 0;
  17.            memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
  18.            return GPS_Result_DataRead;
  19.        }
  20.        pointerToData = strstr(gpsBuffer, "$GPGGA");
  21.        if(pointerToData != NULL)
  22.        {
  23.            gps_DecodeGPGGA_Msg(pointerToData, positionStart);
  24.        }
  25.        pointerToData = strstr(gpsBuffer, "$GPGSA");
  26.        if(pointerToData != NULL)
  27.        {
  28.            gps_DecodeGPGSA_Msg(pointerToData, positionStart);
  29.        }
  30.        pointerToData = strstr(gpsBuffer, "$GPRMC");
  31.        if(pointerToData != NULL)
  32.        {
  33.            gps_DecodeGPRMC_Msg(pointerToData, positionStart);
  34.        }
  35.        pointerToData = strstr(gpsBuffer, "$GPVTG");
  36.        if(pointerToData != NULL)
  37.        {
  38.            gps_DecodeGPVTG_Msg(pointerToData, positionStart);
  39.        }
  40.        displayGPSDataOnScreen();
  41.        receiveWholeLinesCounts_USART6 = 0;
  42.        receiveBufferCount_USART6 = 0;
  43.        memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
  44.        free(pointerToData);
  45.        operationStatus = GPS_Result_DecodeFinish;
  46.     }
  47.     return operationStatus;
  48. }

W funkcji powyżej gdy uda się odczytać pełną ramkę danych układ przechodzi do jej zdekodowania. Dane przekazywane są do funkcji pomocniczych. Można w tej funkcji zastosować wyświetlanie danych po obrobieniu natomiast takie zmiany powodują duże opóźnienie przy wykorzystywaniu przycisków do zmiany wyświetlanych informacji.

Każda z funkcji dekodującej określoną ramkę posiada szereg funkcji pomocniczych. Zostały one osobno przygotowane dla każdej ramki w celu łatwiejszej zmiany w poszczególnych pakietach. Poniżej wykaz przykładowych funkcji do dekodowania pakietu GPGGA.

  1. static void gpgga_UTCTime(char *gpsBuffer);
  2. static void gpgga_Latitude(char * gpsBuffer);
  3. static void gpgga_Longtitude(char *gpsBuffer);
  4. static void gpgga_FixValue(char *gpsBuffer);
  5. static void gpgga_Satellites(char *gpsBuffer);
  6. static void gpgga_Hdop(char *gpsBuffer);
  7. static void gpgga_Altitude(char *gpsBuffer);
  8. static void gpgga_GeoidAboveWGS84(char *gpsBuffer);

Dane można przechowywać w dwóch zmiennych 32 bitowych. W związku z tym że mogą występować wartości ujemne to jedna zmienna powinna być w formacie int32 bitowym. Druga ułamkowa powinna być w formacie uint32_t. Ja ten mechanizm wykorzystałem do wyświetlania wprowadzania danych do funkcji sprintf. Gdzie przed wyświetleniem rozdzielam je na dwie osobne liczby. Przez co pomijam dodanie obsługi zmiennych float w funkcji przygotowującej ramkę do wyświetlenia.

Przykładowe dekodowanie ramki:

  1. static GPS_DecodeOperationStat_TypeDef gps_DecodeGPVTG_Msg(char *gpsBuffer, uint8_t bufferPosition)
  2. {
  3.     volatile GPS_DecodeOperationStat_TypeDef operationStatus = GPS_GPGSA_DecodeError;
  4.     char *pointer;
  5.     pointer = strstr(gpsBuffer, ",");
  6.     if(pointer != NULL)
  7.     {
  8.         //Real course
  9.         if(dataFlag_t.Gps_Gpvtg_Flag_RealCourse)        {   gpvtg_RealCourse(pointer);          }
  10.         pointer = strstr(pointer+1, ",");
  11.         //True track data good info
  12.         if(dataFlag_t.Gps_Gpvtg_Flag_DataValidInfo_1)   {   gpvtg_DataTrueTrack(pointer);       }
  13.         pointer = strstr(pointer+1, ",");
  14.         pointer = strstr(pointer+1, ",");
  15.         //Magnetic track made good
  16.         if(dataFlag_t.Gps_Gpvtg_Flag_DataValidInfo_2 )  {   gpvtg_DataMagneticTrack(pointer);   }
  17.         //Speed in knots
  18.         if(dataFlag_t.Gps_Gpvtg_Flag_Speed )            {   gpvtg_SpeedInKnots(pointer);        }
  19.         pointer = strstr(pointer+1, ",");
  20.         pointer = strstr(pointer+1, ",");
  21.         //Speed in km
  22.         if(dataFlag_t.Gps_Gpvtg_Flag_SpeedInKm )        { gpvtg_SpeedInKm(pointer); }
  23.         pointer = strstr(pointer+1, ",");
  24.         pointer = strstr(pointer+1, ",");
  25.         pointer = strstr(pointer+1, ",");
  26.         //WorkMode
  27.         if(dataFlag_t.Gps_Gpvtg_Flag_WorkMode )         {   gpvtg_WorkMode(pointer); }
  28.         operationStatus = GPS_GPVTG_DecodeOK;
  29.     }
  30.     return operationStatus;
  31. }

Dodatkowo stosuje obsługę panelu dotykowego tak aby można było się przełączać między poszczególnymi pakietami.

Poniżej obsługa panelu dotykowego:

  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  2. {
  3.     static uint16_t x=0;
  4.     static uint16_t y=0;
  5.     static loop = 0;
  6.     char str1[20];
  7.     gps_Button_t *btn = NULL;
  8.     //---------------------------------
  9.     TS_GetState(&TS_State);
  10.     if(TS_State.touchDetected) /* TS_State.touchDetected >= 1 */
  11.     {
  12.         if((!= TS_State.touchX[0]) && (!= TS_State.touchY[0]))
  13.         {
  14.             x = TS_State.touchX[0];
  15.             y = TS_State.touchY[0];
  16.             btn = CheckBtnClicked(&x, &y);
  17.             if(btn != NULL) {
  18.                 btn->Handler(btn->buttonId);
  19.             }
  20.             ROCKTECH_SetFont(&Font12);
  21.             ROCKTECH_SetTextColor(LCD_COLOR_WHITE);
  22.             ROCKTECH_SetBackColor(LCD_BACKGROUND_DEFAULT);
  23.             sprintf(str1,"1: x=%03d; y=%03d\n",x,y);
  24.             ROCKTECH_DisplayString(0, 240, (uint8_t *)str1, LEFT_MODE, 1);
  25.         }
  26.     }
  27.     if(loop == 30)
  28.     {
  29.         if(lastBtnId != btnFlag)
  30.         {
  31.             /* Load new label data */
  32.             displayLabelData(btnFlag);
  33.             lastBtnId = btnFlag;
  34.         }
  35.         displayGPSDataOnScreen();
  36.         loop = 0;
  37.     }
  38.     else
  39.     {
  40.         loop++;
  41.     }
  42. }

Sprawdzanie wciśnięcia klawisza jak i odświeżanie ekranu wykonane jest na przerwaniach od timera. Dzięki temu odświeżanie ekranu jak i reakcja na dotyk występują w stałych, krótkich odstępach czasowych. Jeśli to samo wykonywane było by w pętli while, to czas od wciśnięcia do reakcji byłby różny w zależności od tego w którym momencie nastąpiło kliknięcie na ekran.

Konfiguracja timera jest następująca:

  1. static void MX_TIM6_Init(void)
  2. {
  3.   TIM_MasterConfigTypeDef sMasterConfig;
  4.   htim6.Instance = TIM6;
  5.   htim6.Init.Prescaler = 10000;
  6.   htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  7.   htim6.Init.Period = 200;
  8.   htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  9.   if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  10.   {
  11.     _Error_Handler(__FILE__, __LINE__);
  12.   }
  13.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
  14.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  15.   if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  16.   {
  17.     _Error_Handler(__FILE__, __LINE__);
  18.   }
  19. }

Kolejny post będzie przedstawiał zapis danych do pliku oraz pomiary czasu wykonywania poszczególnych funkcji.

Bibliotekę można pobrać z dysku Google pod tym linkiem.

Opis standardu NEMEA (link).