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.
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.
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 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 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:
- #include <SoftwareSerial.h>
- SoftwareSerial GPS(2, 3); // RX, TX
- void setup()
- {
- Serial.begin(115200);
- while (!Serial) { }
- softSerialGps.begin(9600);
- }
- void loop()
- {
- if (softSerialGps.available())
- {
- Serial.write(softSerialGps.read());
- }
- }
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.
- /* GPS test aplication */
- #include <SoftwareSerial.h>
- SoftwareSerial GPS(2, 3); // RX, TX
- char line1[] = "$GPGGA,160249.000,5004.4736,N,01954.6987,E,1,5,1,91,257.3,M,42.0,M,,*53\r\n";
- char line2[] = "$GPGSA,A,3,31,02,12,05,25,,,,,,,,2.12,1.92,0.92*00\r\n";
- char line3[] = "$GPRMC,160250.000,A,5004.4736,N,01954.7006,E,0.56,80.45,020418,,,A*54\r\n";
- char line4[] = "$GPVTG,80.45,T,,M,0.56,N,1.03,K,A*05\r\n";
- void setup()
- {
- Serial.begin(115200);
- while (!Serial) { }
- GPS.begin(9600);
- }
- void loop()
- {
- GPS.write(line1);
- GPS.write(line2);
- GPS.write(line3);
- GPS.write(line4);
- Serial.write(line1);
- Serial.write(line2);
- Serial.write(line3);
- Serial.write(line4);
- delay(2000);
- }
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:
- char line1[] = "$GPGGA,160249.000,5004.4736,N,01954.6987,E,1,5,1.91,257.3,M,42.0,M,,*53\r\n";
- 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";
- 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";
- 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";
- 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";
- char line2[] = "$GPGSA,A,3,31,02,12,05,25,,,,,,,,2.12,12.92,1,23*00\r\n";
- char line2_1[] = "$GPGSA,M,1,31,02,12,05,25,,,,,,,,12.12,10.92,5.34*00\r\n";
- char line2_2[] = "$GPGSA,A,2,31,02,12,05,25,,,,,,,,23.12,11.92,10.12*00\r\n";
- char line2_3[] = "$GPGSA,M,3,31,02,12,05,25,,,,,,,,52.12,12.92,2.56*00\r\n";
- char line2_4[] = "$GPGSA,A,1,31,02,12,05,25,,,,,,,,12.12,23.92,45.11*00\r\n";
- char line3[] = "$GPRMC,160250.000,A,5004.4736,N,01954.6987,E,0.56,80.45,020418,,,A*54\r\n";
- char line3_1[] = "$GPRMC,082334.000,V,4124.8963,N,08151.6838,W,1.56,30.25,050118,,,A*54\r\n";
- char line3_2[] = "$GPRMC,120032.000,A,2378.1279,N,06523.5732,E,5.56,45.75,150918,,,A*54\r\n";
- char line3_3[] = "$GPRMC,190059.000,V,8721.7519,N,09856.5672,W,2.56,21.25,221118,,,A*54\r\n";
- char line3_4[] = "$GPRMC,023296.000,A,5004.2347,N,04242.6934,E,10.56,54.85,300418,,,A*54\r\n";
- char line4[] = "$GPVTG,80.45,T,,M,2.56,N,1.03,K,A*05\r\n";
- char line4_1[] = "$GPVTG,40.45,T,,M,34.56,N,10.53,K,A*05\r\n";
- char line4_2[] = "$GPVTG,20.45,T,,M,12.56,N,23.78,K,A*05\r\n";
- char line4_3[] = "$GPVTG,83.45,T,,M,23.56,N,09.23,K,A*05\r\n";
- 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:
- #define DISPL_UART6_ON_UART1
- /*
- * @brief: function use for checking receive parameter
- * it is use for checking if 4 lines of data has been receive
- * @param: receiveData: receive byte of information throw uart
- * @ret: none
- * */
- void checkDataReceive(uint8_t receiveData)
- {
- if(receiveData == 0x0D && receiveWholeLinesCounts_USART6 < 4)
- {
- if(strstr((char*)USART6_Buffer, "$GPGGA") != NULL)
- {
- receiveWholeLinesCounts_USART6++;
- }
- }
- }
- /*
- * @brief: function use for checking if receive buffer is full
- * @param: none
- * @ret: none
- * */
- void checkIfBufferFullThenClear(void)
- {
- if(receiveBufferCount_USART6 == (Buffer_Size - 1))
- {
- receiveBufferCount_USART6 = 0;
- receiveWholeLinesCounts_USART6 = 0;
- memset(USART6_Buffer, 0x00, sizeof(Buffer_Size));
- }
- }
- //--------------------------------------------------------------
- #if USART6_USE==1
- void USART6_IRQHandler(void)
- {
- static uint8_t dane;
- //Sprawdzenie czy przerwanie wywolane przez odebranie danych
- if (USART6->ISR & USART_ISR_RXNE)
- {
- dane = ((USART6)->RDR);
- *(USART6_Buffer + receiveBufferCount_USART6) = dane;
- receiveBufferCount_USART6++;
- #ifdef DISPL_UART6_ON_UART1
- usart_SendByte(USART1, dane);
- #endif
- checkDataReceive(dane);
- checkIfBufferFullThenClear();
- }
- USART_INT_ClearAllFlags(USART6, USART6_IRQn);
- }
- #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:
- /* == struct for GPGGA - Global Positioning System Fix Data == */
- typedef struct{
- gps_Time_TypeDef ReadTime; /* UTC time */
- float Latitude; /* Latitude */
- gps_Directions LatitudeDirection; /* N - north; S - south*/
- float Longitude; /* Longitude */
- gps_Directions LongitudeDirection; /* E - east; W - west*/
- gps_FixValue FixValue; /* 0 - Error; 1 - GPS Fix; 2 - DGPS Fix. */
- uint8_t Satellites; /* Number of satellites*/
- float HdopValue; /* HDOP */
- float Altitude; /* Altitude above the sea */
- float GeoidAboveWGS84; /* Height of geoid above WGS84 ellipsoid */
- }gps_GPGGA_TypeDef;
- /* == struct for GPGSA - GPS DOP and active satellites == */
- typedef struct{
- uint8_t WorkMode; /* A - automatic; M - manual */
- uint8_t FixValue; /* 0 - Error; 1 - GPS Fix; 2 - DGPS Fix. */
- uint8_t Satelites[12]; /* Number of satellites use for calculate position */
- float PdopValue; /* PDOP */
- float HdopValue; /* HDOP */
- float VdopValue; /* VDOP */
- }gps_GPGSA_TypeDef;
- /* == struct for GPRMC - Recommended minimum specific GPS/Transit data == */
- typedef struct{
- gps_Time_TypeDef ReadTime; /* UTC time */
- uint8_t DataValidInfo; /* A - OK, V - error */
- float Latitude; /* Latitude */
- gps_Directions LatitudeDirection; /* N - north; S - south*/
- float Longitude; /* Longitude */
- gps_Directions LongitudeDirection; /* E - east; W - west*/
- float SpeedInKnots; /* Speed in knots */
- float RealCourse; /* In radious */
- gps_Date_TypeDef ReadDate; /* Date from gps */
- float Direction; /* Course */
- uint8_t WorkMode; /* A - autonomic, D - dyference, E - esymowany */
- }gps_GPRMC_TypeDef;
- /* struct for GPVTG - Track Made Good and Ground Speed */
- typedef struct{
- float RealCourse; /* In radious */
- uint8_t DataValidInfo_1; /* T - true track made good */
- uint8_t DataValidInfo_2; /* T - true track made good */
- float SpeedInKnots; /* Speed in knots */
- float SpeedInKm; /* Speed in km/h */
- uint8_t WorkMode; /* A - autonomic, D - dyference, E - esymowany */
- }gps_GPVTG_TypeDef;
Aktywacja obrabiania poszczególnych pakietów wykonuje się poprzez ustawienie flagi w strukturze:
- typedef struct{
- #if GPS_GPGGA_USE == 1
- uint8_t Gps_Gpgga_Flag_ReadTime:1;
- uint8_t Gps_Gpgga_Flag_Latitude:1;
- uint8_t Gps_Gpgga_Flag_LatitudeDirection:1;
- uint8_t Gps_Gpgga_Flag_Longitude:1;
- uint8_t Gps_Gpgga_Flag_LongitudeDirection:1;
- uint8_t Gps_Gpgga_Flag_FixValue:1;
- uint8_t Gps_Gpgga_Flag_Satellites:1;
- uint8_t Gps_Gpgga_Flag_HdopValue:1;
- uint8_t Gps_Gpgga_Flag_Altitude:1;
- uint8_t Gps_Gpgga_Flag_GeoidAboveWGS84:1;
- #endif
- #if GPS_GPGSA_USE == 1
- uint8_t Gps_Gpgsa_Flag_WorkMode:1;
- uint8_t Gps_Gpgsa_Flag_FixValue:1;
- uint8_t Gps_Gpgsa_Flag_Satelites:1;
- uint8_t Gps_Gpgsa_Flag_PdopValue:1;
- uint8_t Gps_Gpgsa_Flag_HdopValue:1;
- uint8_t Gps_Gpgsa_Flag_VdopValue:1;
- #endif
- #if GPS_GPRMC_USE == 1
- uint8_t Gps_Gprmc_Flag_ReadTime:1;
- uint8_t Gps_Gprmc_Flag_DataValidInfo:1;
- uint8_t Gps_Gprmc_Flag_Latitude:1;
- uint8_t Gps_Gprmc_Flag_LatitudeDirection:1;
- uint8_t Gps_Gprmc_Flag_Longitude:1;
- uint8_t Gps_Gprmc_Flag_LongitudeDirection:1;
- uint8_t Gps_Gprmc_Flag_Speed:1;
- uint8_t Gps_Gprmc_Flag_RealCourse:1;
- uint8_t Gps_Gprmc_Flag_ReadDate:1;
- uint8_t Gps_Gprmc_Flag_Direction:1;
- uint8_t Gps_Gprmc_Flag_WorkMode:1;
- #endif
- #if GPS_GPVTG_USE == 1
- uint8_t Gps_Gpvtg_Flag_RealCourse:1;
- uint8_t Gps_Gpvtg_Flag_DataValidInfo_1:1;
- uint8_t Gps_Gpvtg_Flag_DataValidInfo_2:1;
- uint8_t Gps_Gpvtg_Flag_Speed:1;
- uint8_t Gps_Gpvtg_Flag_SpeedInKm:1;
- uint8_t Gps_Gpvtg_Flag_WorkMode:1;
- #endif
- }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:
- GPS_DataStatus_TypeDef GPS_CheckDataAvaliable(void)
- {
- char gpsBuffer[Buffer_Size] = {0x00};
- GPS_DataStatus_TypeDef operationStatus = GPS_Result_DataRead;
- uint8_t frameToSearch[5] = {'G', 'P', 'G', 'G', 'A'};
- char *pointerToData;
- if(receiveWholeLinesCounts_USART6 == 4)
- {
- memcpy(gpsBuffer, USART6_Buffer, receiveBufferCount_USART6);
- memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
- volatile uint8_t positionStart = gps_SearchFrameForBuffer(gpsBuffer, frameToSearch,
- sizeof(frameToSearch)/sizeof(frameToSearch[0]), receiveBufferCount_USART6);
- if(positionStart == 0) /* Error no frame in buffer */
- {
- receiveWholeLinesCounts_USART6 = 0;
- receiveBufferCount_USART6 = 0;
- memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
- return GPS_Result_DataRead;
- }
- pointerToData = strstr(gpsBuffer, "$GPGGA");
- if(pointerToData != NULL)
- {
- gps_DecodeGPGGA_Msg(pointerToData, positionStart);
- }
- pointerToData = strstr(gpsBuffer, "$GPGSA");
- if(pointerToData != NULL)
- {
- gps_DecodeGPGSA_Msg(pointerToData, positionStart);
- }
- pointerToData = strstr(gpsBuffer, "$GPRMC");
- if(pointerToData != NULL)
- {
- gps_DecodeGPRMC_Msg(pointerToData, positionStart);
- }
- pointerToData = strstr(gpsBuffer, "$GPVTG");
- if(pointerToData != NULL)
- {
- gps_DecodeGPVTG_Msg(pointerToData, positionStart);
- }
- displayGPSDataOnScreen();
- receiveWholeLinesCounts_USART6 = 0;
- receiveBufferCount_USART6 = 0;
- memset(USART6_Buffer, 0, sizeof(USART6_Buffer));
- free(pointerToData);
- operationStatus = GPS_Result_DecodeFinish;
- }
- return operationStatus;
- }
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.
- static void gpgga_UTCTime(char *gpsBuffer);
- static void gpgga_Latitude(char * gpsBuffer);
- static void gpgga_Longtitude(char *gpsBuffer);
- static void gpgga_FixValue(char *gpsBuffer);
- static void gpgga_Satellites(char *gpsBuffer);
- static void gpgga_Hdop(char *gpsBuffer);
- static void gpgga_Altitude(char *gpsBuffer);
- 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:
- static GPS_DecodeOperationStat_TypeDef gps_DecodeGPVTG_Msg(char *gpsBuffer, uint8_t bufferPosition)
- {
- volatile GPS_DecodeOperationStat_TypeDef operationStatus = GPS_GPGSA_DecodeError;
- char *pointer;
- pointer = strstr(gpsBuffer, ",");
- if(pointer != NULL)
- {
- //Real course
- if(dataFlag_t.Gps_Gpvtg_Flag_RealCourse) { gpvtg_RealCourse(pointer); }
- pointer = strstr(pointer+1, ",");
- //True track data good info
- if(dataFlag_t.Gps_Gpvtg_Flag_DataValidInfo_1) { gpvtg_DataTrueTrack(pointer); }
- pointer = strstr(pointer+1, ",");
- pointer = strstr(pointer+1, ",");
- //Magnetic track made good
- if(dataFlag_t.Gps_Gpvtg_Flag_DataValidInfo_2 ) { gpvtg_DataMagneticTrack(pointer); }
- //Speed in knots
- if(dataFlag_t.Gps_Gpvtg_Flag_Speed ) { gpvtg_SpeedInKnots(pointer); }
- pointer = strstr(pointer+1, ",");
- pointer = strstr(pointer+1, ",");
- //Speed in km
- if(dataFlag_t.Gps_Gpvtg_Flag_SpeedInKm ) { gpvtg_SpeedInKm(pointer); }
- pointer = strstr(pointer+1, ",");
- pointer = strstr(pointer+1, ",");
- pointer = strstr(pointer+1, ",");
- //WorkMode
- if(dataFlag_t.Gps_Gpvtg_Flag_WorkMode ) { gpvtg_WorkMode(pointer); }
- operationStatus = GPS_GPVTG_DecodeOK;
- }
- return operationStatus;
- }
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:
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- static uint16_t x=0;
- static uint16_t y=0;
- static loop = 0;
- char str1[20];
- gps_Button_t *btn = NULL;
- //---------------------------------
- TS_GetState(&TS_State);
- if(TS_State.touchDetected) /* TS_State.touchDetected >= 1 */
- {
- if((x != TS_State.touchX[0]) && (y != TS_State.touchY[0]))
- {
- x = TS_State.touchX[0];
- y = TS_State.touchY[0];
- btn = CheckBtnClicked(&x, &y);
- if(btn != NULL) {
- btn->Handler(btn->buttonId);
- }
- ROCKTECH_SetFont(&Font12);
- ROCKTECH_SetTextColor(LCD_COLOR_WHITE);
- ROCKTECH_SetBackColor(LCD_BACKGROUND_DEFAULT);
- sprintf(str1,"1: x=%03d; y=%03d\n",x,y);
- ROCKTECH_DisplayString(0, 240, (uint8_t *)str1, LEFT_MODE, 1);
- }
- }
- if(loop == 30)
- {
- if(lastBtnId != btnFlag)
- {
- /* Load new label data */
- displayLabelData(btnFlag);
- lastBtnId = btnFlag;
- }
- displayGPSDataOnScreen();
- loop = 0;
- }
- else
- {
- loop++;
- }
- }
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:
- static void MX_TIM6_Init(void)
- {
- TIM_MasterConfigTypeDef sMasterConfig;
- htim6.Instance = TIM6;
- htim6.Init.Prescaler = 10000;
- htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
- htim6.Init.Period = 200;
- htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
- if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
- if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
- {
- _Error_Handler(__FILE__, __LINE__);
- }
- }
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).