Digilent Pmod i STM32 (cz. 4) – PmodOLED, PmodGPS i Pmod8LD
PmodGPS
PmodGPS wyposażono w moduł Gms-u1LP z wbudowaną anteną. Moduł ten zapewnia czułość na poziomie -165 dBm, a także dokładność pozycjonowania 3 m. Domyślnie moduł Gms-u1LP wysyła dane w postaci zdań zgodnych z protokołem NMEA za pośrednictwem interfejsu UART. Typy wysyłanych zdań zamieszczono w tabeli 3. Dodatkowo dostępne są dwa sygnały: 3DF i 1PPS. Pierwszy z nich informuje o stanie pozycjonowania zmieniając stan co sekundę podczas ustalania pozycji i trzymając stan niski jeżeli ustalono pozycję (2D lub 3D). Natomiast drugi sygnał – 1PPS zapewnia synchronizację z wewnętrznym czasem uzyskanym z GPS dostarczając impulsów o długości 100 ms na początku każdej sekundy.
Tabela 3. Zdania NMEA wysyłane przez PmodGPS
Typ | Opis |
GGA | Czas i pozycja |
GSA | Aktywne satelity GNSS i rozmycie precyzji (DOP) |
GSV | Widoczne satelity GNSS |
RMC | Zalecane minimalne dane GNSS |
VTG | Kurs nad ziemią i prędkość względem ziemi |
Protokół NMEA
Zdania w protokole NMEA są w postaci tekstowej. Zaczynają się od prefiksu $GP, następnie występują typ zdania i lista wartości oddzielonych od siebie przecinkami. Najważniejszym zdaniem, z punktu widzenia danych niezbędnych do nawigacji, jest RMC (Recommended Minimum Navigation Information). Zawiera ono takie informacje jak czas, współrzędne geograficzne, prędkość oraz kurs. Szczegółową strukturę zdania RMC przedstawiono w tabeli 4. Natomiast opis pozostałych zdań zawierającym m.in. wysokość nad poziomem morza (GGA), widoczne satelity (GSV) można znaleźć w dokumentacji modułu PmodGPS: https://reference.digilentinc.com/_media/reference/pmod/pmodgps/pmodgps-gms-u1lp_rm.pdf
Tabela 4. Struktura zdania RMC
Pole | Opis |
Message ID | Nagłówek zdania: $GPRMC |
UTC Time | Aktualna godzina w postaci hhmmss.sss |
Status | Stan danych – ważne (A), lub nie (V) |
Latitude | Szerokość geograficzna w stopniach (d) i minutach (m) : ddmm.mmmm |
N/S indicator | Szerokość geograficzna północna (N) lub południowa (S) |
Longitude | Długość geograficzna w stopniach (d) i minutach (m) : ddmm.mmmm |
E/W indicator | Długość geograficzna wschodnia (E) lub zachodnia (W) |
Speed | Prędkość względem ziemi w węzłach |
Course | Kurs rzeczywisty w stopniach |
Date | Aktualna data w postaci ddmmyy |
Magnetic variation | Deklinacja magnetyczna w stopniach |
E/W indicator | Deklinacja magnetyczna wschodnia (E) lub zachodnia (W) |
Mode | Tryb autonomiczny (A), różnicowy (D), szacowany (E) |
Checksum | Suma kontrolna |
Do modułu Gms-u1LP można także wysyłać komendy NMEA konfigurujące urządzenie. Mają one określoną strukturę, przedstawioną w tabeli 5. Jedyną komendą używaną w przykładzie jest odczyt wersji firmware’u znajdującego się w module: „$PMTK605*31\r\n”. W odpowiedzi na jej wysłanie, moduł PmodGPS odpowiada pakietem „$PMTK705,AXN_2.31_3339_13082100,5458,PA6H,1.0*69\r\n”, zawierającym nazwę i wersję oprogramowania (AXN_2.31_3339_13082100).
Tabela 5. Struktura komendy NMEA
Pole | Opis |
Preamble | Preambuła „$” |
Talker ID | Identyfikator komendy „PMTK” |
Packet Type | Typ pakietu o wartościach od „000” do „999” |
Data Field | Opcjonalne pola danych rozdzielone przecinkami |
Checksum | Suma kontrolna poprzedzona znakiem „*” |
Połączenie z płytką KAmeleon
PmodGPS wyposażono w złącze typu UART typu 4 z pinami 3DF i 1PPS w miejsce CTS i RTS. W przykładzie moduł podłączono do złącza Arduino udostępniającego interfejs USART3 i linie GPIO. Listę pinów złącza J1 modułu PmodGPS i odpowiadające im piny złącza Arduino przedstawiono w tabeli 6.
Tabela 6. Podłączenie modułu PmodGPS do złącza ARDUINO zestawu KAmeleon
Sygnał | Numer pinu PmodGPS | Numer pinu KAmeleon ARDUINO CONNECTOR | Pin mikrokontrolera |
3DF | 1 | D2 | PD6 |
RX | 2 | D1 | PC4 |
TX | 3 | D0 | PC5 |
1PPS | 4 | D6 | PD10 |
GND | 5 | GND | – |
VCC | 6 | +3,3 | – |
Kod projektu
Konfiguracja modułu PmodGPS znajduje się w dwóch funkcjach w pliku src/PmodGPS.c. Pierwszą z nich, PmodGPS_Config przedstawiono na listingu 2. Ustawia ona piny 1PPS i 3DF jako przerwania zewnętrzne, a także konfiguruje interfejs USART3 według domyślnych ustawień modułu (baudrate 9600, 8-bitowe dane, 1 bit stopu, brak parzystości). Konfiguracja pinów dla interfejsu UART odbywa się w drugiej funkcji – PmodGPS_HAL_UART_MspInit, pokazanej na listingu 3. Funkcja ta jest wywoływana podczas inicjalizacji interfejsu UART przez bibliotekę STM32Cube za pośrednictwem innej funkcji – HAL_UART_MspInit, znajdującej się w pliku main.c. Ze względu na to, że w przykładzie wykorzystano dwa interfejsy UART, funkcja HAL_UART_MspInit decyduje o tym, który interfejs powinien być skonfigurowany. Plik src/PmodGPS.c zawiera ponadto funkcje odpowiedzialne za wymianę danych z modułem PmodGPS: PmodGPS_Write oraz PmodGPS_Read, jednak są one jedynie wrapperami na nieblokujące funkcje transmisji i odczytu danych korzystających z przerwań: HAL_UART_Transmit_IT i HAL_UART_Receive_IT.
Listing 2. Konfiguracja interfejsu UART dla PmodGPS
void PmodGPS_Config(void) { __HAL_RCC_GPIOD_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_10; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0); HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 1); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); pmodGpsUart.Instance = USART3; pmodGpsUart.Init.BaudRate = 9600; pmodGpsUart.Init.WordLength = UART_WORDLENGTH_8B; pmodGpsUart.Init.StopBits = UART_STOPBITS_1; pmodGpsUart.Init.Parity = UART_PARITY_NONE; pmodGpsUart.Init.Mode = UART_MODE_TX_RX; pmodGpsUart.Init.HwFlowCtl = UART_HWCONTROL_NONE; pmodGpsUart.Init.OverSampling = UART_OVERSAMPLING_16; pmodGpsUart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; HAL_UART_Init(&pmodGpsUart); }
Listing 3. Konfiguracja GPIO dla interfejsu UART
void PmodGPS_HAL_UART_MspInit(UART_HandleTypeDef *huart) { // Enable the clocks for GPIO pins used by the UART3 port (PC4, PC5). __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3; GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // Enable the interrupts generated by USART3 port. HAL_NVIC_SetPriority(USART3_IRQn, 1, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); }
Zmienne globane do odbioru danych
Odbiór danych z modułu PmodGPS odbywa się za pośrednictwem szeregu zmiennych globalnych przedstawionych na listingu 4. Do przechowywania danych używane są bufory typu DataBuffer zawierające maksymalnie po 128 bajtów danych oraz index wskazujący zajętość bufora. W przykładzie użyto dwa bufory – jeden zapisywany przychodzącymi danymi, a drugi zawierający gotowy pakiet danych do przetworzenia. Indeks aktualnie zapisywanego bufora znajduje się w zmiennej currentDataBuffer. Natomiast flaga informująca o zakończeniu odbioru pakietu znajduje się w zmiennej dataReady. Wymienione zmienne są współdzielone przez funkcje main, a także HAL_UART_RxCpltCallback, w pliku main.c. Funkcja main, po wykonaniu podstawowej inicjalizacji procesora, a także wyczyszczeniu buforów, wysyła opisaną wcześniej komendę odczytu wersji firmware’u. Następnie uruchamia asynchroniczny odczyt danych do pierwszego bufora i przechodzi w stan oczekiwania na odebranie pakietu. Odebranie pakietu jest sygnalizowane przez flagę dataReady, po otrzymaniu której cały pakiet wypisywany jest na port szeregowy. Całą procedurę konfiguracji i wypisywania danych przedstawiono na listingu 5.
Listing 4. Zmienne używane do odbioru danych z modułu PmodGPS
#define DATA_BUFFERS_COUNT 2 typedef struct { uint8_t buffer[128]; uint32_t index; } DataBuffer; DataBuffer dataBuffers[DATA_BUFFERS_COUNT]; uint32_t currentDataBuffer = 0; uint8_t dataReady = 0;
Listing 5. Funkcja main przykładu
int main(void) { HAL_Init(); SystemClock_Config(); Led_Config(); Serial_Config(); for(uint32_t i = 0; i < DATA_BUFFERS_COUNT; i++) dataBuffers[i].index = 0; PmodGPS_Config(); PmodGPS_Write("$PMTK605*31\r\n", 13); PmodGPS_Read(dataBuffers[0].buffer, 1); while(1) { while(dataReady == 0) __NOP(); dataReady = 0; // The buffer before the current one contains the data received from PmodGPS. uint32_t readyDataBuffer = (currentDataBuffer > 0) ? (currentDataBuffer - 1) : (DATA_BUFFERS_COUNT - 1); // Write the data from the buffer to the serial port and clear the buffer. Serial_Write((char*)dataBuffers[readyDataBuffer].buffer, dataBuffers[readyDataBuffer].index + 1); dataBuffers[readyDataBuffer].index = 0; } }
Odbiór danych
Dane z modułu PmodGPS odbierane są w przerwaniu od portu USART3, którego obsługa znajduje się na listingu 6. W pierwszej kolejności sprawdzane jest czy odebrano cały pakiet, co jest jednoznaczne z otrzymaniem znaku nowej linii: ‘\n’. Jeżeli tak, to ustawiana jest flaga dataReady i zmieniany jest bufor do odczytu. W przeciwnym razie przesuwany jest jedynie indeks obecnego bufora. Natomast po zakończonym odbiorze inicjalizowany jest odczyt kolejnego znaku do aktualnego bufora.
Listing 6. Obsługa przerwania interfejsu UART
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(dataBuffers[currentDataBuffer].buffer[dataBuffers[currentDataBuffer].index] == '\n') { dataReady = 1; currentDataBuffer++; if(currentDataBuffer == DATA_BUFFERS_COUNT) currentDataBuffer = 0; } else { dataBuffers[currentDataBuffer].index++; } PmodGPS_Read(&dataBuffers[currentDataBuffer].buffer[dataBuffers[currentDataBuffer].index], 1); }
Funkcję obsługi przerwania od dwóch sygnałów modułu – 1PPS i 3DF – zaimplementowano w funkcji HAL_GPIO_EXTI_Callback. W zależności od tego, który sygnał wywołał przerwanie, zmieniany jest stan jednej z diod znajdujących się na płytce KAmeleon, dzięki czemu możliwe jest obserwowanie stanu obu linii.
Moduł PmodGPS podłączony do płyty KAmeleon przedstawiono na fotografii 4.