KursyPoradnikiInspirujące DIYForum

Kurs STM32L4 – #13 – czujnik ciśnienia, pomiar wysokości (I2C)

Kurs STM32L4 – #13 – czujnik ciśnienia, pomiar wysokości (I2C)

Czujniki ciśnienia mają wiele zastosowań, można je np. wykorzystać do pomiaru wysokości – taką funkcję pełnią m.in. w zegarkach sportowych.

W tej części kursu STM32 zajmiemy się obsługą popularnego czujnika LPS25HB, który pozwala na pomiar ciśnienia, wysokości oraz temperatury.

Czego dowiesz się z tej części kursu STM32L4?

Tym razem zajmiemy się obsługą modułu, na którego pokładzie znajduje się nowoczesny i precyzyjny czujnik ciśnienia atmosferycznego, czyli cyfrowy barometr. Zaczniemy od omówienia najważniejszych informacji na temat LPS25HB i nawiązania komunikacji przez I2C, a skończymy na pomiarach ciśnienia względnego i bezwzględnego, pomiarze wysokości oraz kalibracji. Przy okazji będzie to dobry powód do szybkiej powtórki z fizyki.

Dlaczego czujniki ciśnienia nie są nudne?

Czujniki ciśnienia kojarzone są przez większość osób ze stacjami pogodowymi, które wskazują aktualne ciśnienie – funkcja ta może być przydatna, ale nie jest widowiskowa. Warto jednak pamiętać, że takie sensory używane są również np. do pomiaru wysokości.

Pomiar ciśnienia kojarzony jest często ze stacjami pogodowymi – większość popularnych modeli nie posiada jednak takiej opcji, bo pomiar ten jest stosunkowo skomplikowany

Pomiar ciśnienia kojarzony jest często ze stacjami pogodowymi – większość popularnych modeli nie posiada jednak takiej opcji, bo pomiar ten jest stosunkowo skomplikowany

Pomiar wysokości to funkcja używana w wielu urządzeniach – od pojazdów latających po popularne zegarki sportowe, dzięki którym można się dowiedzieć, na jakie wzniesienie udało nam się właśnie wjechać. Moduły te są tak dokładne, że bez problemu można za ich pomocą wykryć podniesienie urządzenia nawet o kilkadziesiąt centymetrów. 

Barometry są ważnym elementem wielu zegarków sportowych

Barometry są ważnym elementem wielu zegarków sportowych

Dlatego nawet jeśli tematyka pomiaru ciśnienia wydaje się komuś nudna, to zdecydowanie warto przebrnąć przez podstawy, aby później wykorzystywać te informacje w znacznie ciekawszych projektach niż stacja pogodowa. Na wstępie trzeba jednak zdać sobie sprawę, że pomiar ciśnienia to trudny temat, a efekty uzyskiwane podczas wykonywania opisanych tutaj ćwiczeń będą różniły się zależnie od miejsca, w którym się znajdujemy, aktualnej pogody oraz wielu innych czynników.

LPS25HB – czujnik ciśnienia i temperatury

Podczas wykonywania ćwiczeń z tej części kursu STM32L4 będziemy korzystać z modułu firmy POLOLU. Został on wyposażony w czujnik ciśnienia LPS25HB oraz szereg dodatkowych elementów, które są niezbędne do poprawnej pracy tego układu – zaliczają się do nich m.in. rezystory pociągające na liniach I2C (nie musimy więc dodawać ich samodzielnie).

Moduł z czujnikiem LPS25HB

Moduł z czujnikiem LPS25HB

Co ciekawe, producentem samego czujnika LPS25HB jest STMicroelectronics, czyli ta sama firma, która odpowiada za opisywane w tym kursie mikrokontrolery STM32L4. Wszystkie informacje na temat czujnika znajdziemy w nocie katalogowej, mającej aż 50 stron.

Fragment dokumentacji czujnika LPS25HB

Fragment dokumentacji czujnika LPS25HB

Długość dokumentacji podpowiada już, że czujnik ten jest nieco bardziej skomplikowany, niż mogłoby się wydawać. Dlatego w tym kursie nie omówimy wszystkich możliwości LPS25HB – skupimy się głównie na realizacji możliwie prostego i uniwersalnego mechanizmu odczytu danych z tego czujnika za pomocą I2C, a następnie zajmiemy się obróbką tych danych. W ramach ciekawostki warto jednak wiedzieć, że z układem tym można również komunikować się za pomocą SPI.

Gotowe zestawy do kursów Forbota

 Komplet elementów  Gwarancja pomocy  Wysyłka w 24h

Zamów zestaw elementów i wykonaj ćwiczenia z tego kursu! W komplecie płytka NUCLEO-L476RG oraz m.in. wyświetlacz graficzny, joystick, enkoder, czujniki (światła, temperatury, wysokości, odległości), pilot IR i wiele innych.

Zamów w Botland.com.pl »

Komunikacja z czujnikiem LPS25HB

W poprzedniej części kursu omówiliśmy sposób komunikacji poprzez I2C na bazie pamięci EEPROM. Teraz bez problemu możemy wykorzystać zdobytą wcześniej wiedzę do pracy z nowym czujnikiem. Na początek podłączenie – w związku z tym, że korzystamy z gotowego modułu, nie musimy dodawać żadnych innych elementów (wystarczy zasilanie z 3,3 V i linie sygnałowe).

Schemat ideowy i montażowy podłączenia czujnika LPS25HB do STM32L4

Schemat ideowy i montażowy podłączenia czujnika LPS25HB do STM32L4

Jeśli zajrzymy do noty katalogowej, to zobaczymy, że komunikacja z LPS25HB przebiega podobnie do tego, co znamy z poprzedniej części kursu. Główna różnica to inny adres układu oraz fakt, że wysyłamy adres rejestru zamiast adresu pamięci EEPROM. Poprzednio używaliśmy układu 24AA01, który posiadał adres 0xA0. Tym razem natomiast posługujemy się układem, który może korzystać z adresu 0xB8 lub 0xBA – wszystko zależy od stanu wyprowadzenia SDO/SA0. Na module POLOLU wyprowadzenie to zostało podłączone przez rezystor z dodatnią szyną zasilania, a to oznacza, że adres układu to 0xBA.

Schemat modułu POLOLU-2867 z czujnikiem ciśnienia LPS25HB

Schemat modułu POLOLU-2867 z czujnikiem ciśnienia LPS25HB

Po podłączeniu czujnika możemy przejść do programowania. Zaczynamy od projektu z STM32L476RG, który pracuje z częstotliwością 80 MHz. Uruchamiamy debugger i USART2 w trybie asynchronicznym. Do tego uruchamiamy I2C1 z domyślnymi ustawieniami. Zaznaczamy również w opcjach projektu, że CubeMX ma wygenerować osobne pliki dla wszystkich modułów. Następnie (już standardowo) dodajemy przekierowanie printf na UART – wystarczy dodanie pliku nagłówkowego:

oraz kodu zbliżonego do poniższego:

Na początek tworzymy stałą przechowującą adres naszego modułu – używanie tzw. magicznych liczb to niezbyt dobra praktyka, wykorzystanie zdefiniowanej wartości będzie znacznie lepszym rozwiązaniem.

W poprzedniej części kursu korzystaliśmy z funkcji HAL_I2C_Mem_Read oraz HAL_I2C_Mem_Write, dzięki którym odczytywaliśmy dane z pamięci EEPROM i zapisywaliśmy je tam. Te same funkcje będą również przydatne podczas komunikacji z czujnikiem ciśnienia.

Różnica jest tylko taka, że poprzednio czytaliśmy dane z komórek pamięci, do których wcześniej sami zapisywaliśmy dane, a teraz będziemy odczytywać informacje z rejestrów, w których przetrzymywane są informacje o konfiguracji czujnika i o aktualnych odczytach.

Opis rejestrów znajdziemy oczywiście w dokumentacji czujnika (oto fragment tabeli):

Opis niektórych rejestrów dostępnych w czujniku LPS25HB

Opis niektórych rejestrów dostępnych w czujniku LPS25HB

Standardowo najlepiej takie dane zapisać w kodzie w formie definicji. Na początek wystarczy kilka poniższych rejestrów, dzięki którym sprawdzimy komunikację z układem, dokonamy odpowiednich ustawień i odczytamy ciśnienie oraz temperaturę – oczywiście za chwilę omówimy te rejestry.

Oprócz tego przyda nam się funkcja pomocnicza, dzięki której łatwo odczytamy informacje z danego rejestru. Jest to zabieg wyłącznie kosmetyczny, abyśmy nie musieli za każdym razem ręcznie wpisywać pełnego wywołania funkcji HAL_I2C_Mem_Read.

Test komunikacji z czujnikiem LPS25HB

Zaczniemy od sprawdzenia, czy nasz układ w ogóle działa i czy komunikacja przebiega prawidłowo. Do tego celu wykorzystamy rejestr WHO_AM_I, który znajduje się pod adresem 0x0F. Rejestr ten powinien zwrócić 0xBD. Jeśli tak się stanie, to mamy pewność, że komunikacja z LPS25HB działa poprawnie.

Rejestr WHO_AM_I w dokumentacji czujnika LPS25HB

Rejestr WHO_AM_I w dokumentacji czujnika LPS25HB

Możemy teraz napisać program testujący komunikację z czujnikiem. Wyświetlamy komunikat w konsoli, a następnie wywołujemy funkcję, która ma odczytać zawartość rejestru WHO_AM_I. Jeśli odpowiedź będzie zgodna z dokumentacją, to wyświetlamy komunikat o znalezieniu czujnika, inna odpowiedź to sygnał, że coś jest źle – wyświetlamy komunikat błędu wraz z podaniem odczytanej wartości.

Wystarczy, że kod ten wywołamy raz (przed pętlą while). Jeśli wszystko przebiegnie poprawnie, na ekranie powinien wyświetlić się poniższy komunikat. W przypadku błędu warto sprawdzić połączenia oraz konfigurację projektu.

Nawiązanie komunikacji z czujnikiem LPS25HB

Nawiązanie komunikacji z czujnikiem LPS25HB

Tego typu test jest bardzo ważny – pozwala nam się upewnić, że komunikacja z czujnikiem działa poprawnie, dzięki czemu możemy też zweryfikować model podłączonego czujnika. Jednak jeśli przy takim programie czujnik zostanie odłączony, to układ się „zawiesi” – wszystko dlatego, że funkcja odczytująca dane jako tzw. timeout ma podane HAL_MAX_DELAY (później to zmienimy).

Odczyt temperatury z czujnika LPS25HB

Po włączeniu zasilania czujnik LPS25HB przechodzi w tryb uśpienia, dzięki czemu pobiera mało prądu. Musimy więc pamiętać, aby wybudzić go przed wykonaniem pomiarów. Do tego potrzebny jest kolejny rejestr, czyli CTRL_REG1.

Rejestr CTRL_REG1 w dokumentacji czujnika LPS25HB

Rejestr CTRL_REG1 w dokumentacji czujnika LPS25HB

Domyślnie wszystkie bity tego rejestru są wyzerowane, w tym bit PD, który odpowiada właśnie za stan pracy czujnika – zero oznacza uśpienie, a logiczna jedynka to obudzenie układu. Ważne są dla nas też bity ODR1 i ODR2. Ich znaczenie zostało opisane w dokumentacji za pomocą poniższej tabeli.

Znaczenie bitów ODR1 i ODR2 z rejestru CTRL_REG1

Znaczenie bitów ODR1 i ODR2 z rejestru CTRL_REG1

Domyślna wartość tych bitów oznacza, że pomiary wykonywane są „na żądanie”, ale LPS25HB może także działać w trybie automatycznego pomiaru z zadaną częstotliwością – i z tej opcji chcemy właśnie skorzystać. Aby ustawić te bity rejestru CTRL_REG1, użyjemy oczywiście funkcji HAL_I2C_Mem_Write, ale dla wygody zdefiniujemy sobie tutaj również funkcję pomocniczą.

Wybudzenie układu z uśpienia oraz ustawienie odpowiedniej częstotliwości pomiaru uzyskamy, jeśli do wspomnianego rejestru zapiszemy binarnie 0b11000000 (heksadecymalnie 0xC0). A zatem wystarczy, że wywołamy w programie (po sprawdzeniu rejestru WHO_AM_I) poniższy kod:

Po uruchomieniu tego programu czujnik zostanie obudzony, a następnie włączony zostanie pomiar ciśnienia i temperatury z częstotliwością 25 Hz. Zapis 0xC0 jest jednak mało czytelny – dla wygody można dodać do programu definicje bitów z rejestru CTRL_REG1:

Wówczas dane do tego rejestru możemy zapisać w zdecydowanie bardziej przyjaznej wersji. Poniższy zapis należy interpretować tak, że do rejestru CTRL_REG1 wstawiamy „jedynki” na miejscu bitu opisanego jako PD oraz ODR2 – pozostałe bity pozostają niezmienione (tutaj zera). W zapisie tym wykorzystany jest operator alternatywy bitowej, czyli „|”.

Tworzenie programów w taki sposób to na pewno dobra praktyka, wróćmy jednak do najważniejszego, czyli do komunikacji z czujnikiem. Po ustawieniu konfiguracji dodaliśmy jeszcze małe opóźnienie. Pomiary będą wykonywane z częstotliwością 25 Hz, dlatego jeśli poczekamy 100 ms, to wynik powinien być już dla nas dostępny.

Teraz możemy przejść do odczytu temperatury. Jej aktualną wartość znajdziemy łącznie w „aż” dwóch rejestrach: TEMP_OUT_L (0x2B) oraz TEMP_OUT_H (0x2C). Wynikiem jest liczba 16-bitowa w tzw. kodzie uzupełnieniowym do 2. Jest to nic innego jak najzwyklejszy int16_t. Rejestry są jednak 8-bitowe, więc czujnik dzieli pomiar na dwa osobne rejestry. W związku z tym w celu poznania zmierzonej temperatury musimy dokonać odczytu z dwóch rejestrów, a następnie połączyć te dane, aby utworzyć 16-bitową wartość.

Tutaj pomocna jest mała sztuczka – okazuje się, że można odczytywać lub zapisywać wiele rejestrów jednocześnie, jeśli mają one kolejne adresy. Jak widzimy, zapewne nieprzypadkowo TEMP_OUT_H ma numer o jeden większy niż TEMP_OUT_L, dzięki czemu możemy łatwo odczytać obie wartości naraz. Jest to cecha tego konkretnego czujnika (a nie interfejsu I2C) – informacji na temat tego typu ułatwień trzeba zawsze szukać w notach katalogowych konkretnych elementów.

W celu dokonania takiego pomiaru wystarczy odwołać się do danego rejestru z ustawionym na jeden najwyższym bitem. Weźmy przykładowo rejestr TEMP_OUT_L, czyli 0x2B. Wartość tę binarnie da się zapisać jako 00101011. Jeśli chcielibyśmy od razu odczytać kolejny rejestr, musielibyśmy odwołać się do rejestru 10101011. Najprościej można zrobić to w kodzie za pomocą alternatywy bitowej. Taka operacja może więc przyjąć postać LPS25HB_TEMP_OUT_L | 0x80. Zapis ten sprawi, że odwołamy się właśnie do 10101011 zamiast do 00101011 – czujnik będzie wtedy wiedział, że ma odesłać 2 bajty.

Finalnie kod, który wyświetla informacje o znalezionym czujniku, wybudza go, włącza automatyczne pomiary i wyświetla aktualną temperaturę, powinien wyglądać tak jak poniżej:

Po uruchomieniu takiego kodu otrzymamy informację, że temperatura to przykładowo… −7230. To nie wina błędnego pomiaru – po prostu czujnik nie zwraca temperatury w stopniach Celsjusza. Otrzymaną wartość musimy przeliczyć zgodnie z formułą, która dostępna jest w dokumentacji.

Informacja z dokumentacji o przeliczaniu wyniku na stopnie Celsjusza

Informacja z dokumentacji o przeliczaniu wyniku na stopnie Celsjusza

Musimy więc zmienić linijkę wyświetlającą temperaturę na następującą:

Teraz wynik będzie wyświetlany jako liczba zmiennoprzecinkowa, musimy tylko włączyć obsługę takich liczb za pomocą printf (Project > Properties > C/C++ Build > Settings > MCU Settings > Use float with printf...). Po uruchomieniu tej wersji programu otrzymamy już poprawną temperaturę.

Pomiar temperatury za pomocą czujnika LPS25HB

Pomiar temperatury za pomocą czujnika LPS25HB

Nie przeprowadzaliśmy kalibracji czujnika, więc pomiar może odbiegać o kilka stopni od wartości rzeczywistej (ale można to wyregulować offesetem – więcej w dokumentacji), jednak uzyskana wartość powinna być przynajmniej zbliżona do rzeczywistej.

Pomiar ciśnienia za pomocą czujnika LPS25HB

Ciśnienie odczytamy z trzech rejestrów: PRESS_OUT_XL, PRESS_OUT_L oraz PRESS_OUT_H. Wynikiem będzie więc 24-bitowa liczba ze znakiem. W języku C nie znajdziemy typu odpowiadającemu takiej liczbie, ale możemy użyć zwykłej liczby 32-bitowej, czyli int32_t.

Przy domyślnych ustawieniach czujnika ciśnienie nigdy nie powinno przyjmować ujemnych wartości, dlatego nie musimy przejmować się znakiem. Wystarczy więc, że najwyższe 8 bitów uzupełnimy zerami, i wszystko powinno już działać. Odczyt ciśnienia wygląda podobnie do odczytu temperatury – dodajemy więc do naszego programu następujące linie:

Warto zwrócić uwagę na dwa szczegóły. Po pierwsze, zmiennej pressure przypisujemy początkową wartość 0. Później wczytujemy tylko 3 bajty, więc najwyższy bajt tej zmiennej nie będzie zmieniany. Gdybyśmy nie ustawili wartości zmiennej na 0, wartość tego bajtu mogłaby być przypadkowa.

Druga zmiana to wartość 3 w parametrze wywołania HAL_I2C_Mem_Read. Zmienna ma 32 bity, czyli 4 bajty, my jednak odczytujemy tylko wartość 24-bitową, więc nie możemy użyć tutaj wywołania sizeof, dlatego ręcznie wpisaliśmy informację o 3 bajtach.

Po uruchomieniu kod zadziała, ale pokaże inną wartość, niż oczekujemy, bo będzie to np. 4186431. Łatwo odgadnąć, że również tym razem wynik wymaga odpowiedniego przeliczenia. Tym razem formuła jest o wiele łatwiejsza, wystarczy podzielić pomiar przez 4096. 

Tym razem uzyskamy już „poprawny” wynik:

Pomiar ciśnienia za pomocą czujnika LPS25HB

Pomiar ciśnienia za pomocą czujnika LPS25HB

Wynik jest „poprawny”, bo coś mierzymy, ale nie omówiliśmy jeszcze jednostki pomiaru, a do tego zupełnie nie wiemy, jak interpretować taki pomiar. Tutaj właśnie kończy się większość poradników na temat czujników ciśnienia (wyświetlenie pomiarów i koniec) – dla nas to dopiero początek! Jednak zanim przejdziemy dalej, zróbmy porządek z kodem – pora na własną bibliotekę.

Biblioteka dla czujnika LPS25HB

Zacznijmy od dodania pliku nagłówkowego lps25hb.h, który powinien mieć poniższą treść:

Plik nagłówkowy stm32l4xx.h włączamy tylko po to, aby mieć dostęp do typu HAL_StatusTypeDef. Jest to typ wyliczeniowy – użyjemy go, aby zwrócić informację o obecności lub braku naszego czujnika. Jeśli odczyt rejestru WHO_AM_I zwróci oczekiwaną wartość, to funkcja lps25hb_init będzie zwracała HAL_OK, a w przypadku błędu – HAL_ERROR. Pozostałe dwie funkcje posłużą nam do odczytywania aktualnej temperatury oraz ciśnienia.

Nasza biblioteka udostępnia bardzo prosty w użyciu „interfejs”. Czas wykorzystać napisany wcześniej kod do stworzenia kodu biblioteki. Wszystkie funkcje pomocnicze koniecznie definiujemy ze słówkiem kluczowym static – dzięki temu nie będą one widoczne poza naszym modułem. To dobra praktyka, dzięki której unikamy konfliktu funkcji z różnych bibliotek (pisaliśmy już o tym wcześniej).

Tworzymy zatem plik lps25hb.c i wstawiamy do niego poniższą treść:

Większość kodu jest identyczna z zastosowanym poprzednio, zmienione zostały jednak czasy oczekiwania używane w HAL_I2C_Mem_Read/Write. Poprzednio było to HAL_MAX_DELAY, tym razem chcemy jednak, aby nasz program nie zawieszał się w przypadku braku odpowiedzi. Dlatego zdefiniowaliśmy stałą TIMEOUT, dzięki której będziemy oczekiwać tylko 100 ms.

Teraz możemy wrócić do pliku głównego, czyli main.c. Usuwamy z niego poprzednio napisany kod i włączamy bibliotekę. Zatem zaczynamy od dodania pliku nagłówkowego:

Natomiast program główny zmienimy na następujący:

Teraz nasz program sprawdza, czy udało się nawiązać połączenie z czujnikiem, i jeśli jest ono poprawne, to co sekundę wyświetla aktualną temperaturę i aktualne ciśnienie. Obsługa błędów nie jest jeszcze idealna, ale działa trochę lepiej niż wcześniejsza wersja.

Efekt działania najnowszej wersji programu

Efekt działania najnowszej wersji programu

Jeśli odłączymy zasilanie czujnika i zresetujemy program, to tym razem otrzymamy w konsoli informacje o błędzie komunikacji (dzięki temu, że nasz timeout to tylko 100 ms). Program się zawiesza, ale przynajmniej wyświetla informację o przyczynie tego błędu.

Interpretacja pomiarów ciśnienia

Spróbujemy zagłębić się dokładniej w tematykę pomiarów ciśnienia, bo nie jest to wcale takie proste, jak mogłoby się wydawać, ale opanowanie tych tematów pozwoli na wykorzystanie tego czujnika do znacznie ciekawszych operacji – przy okazji omówimy jeszcze kilka kwestii związanych z LPS25HB.

Jednostki ciśnienia atmosferycznego

Zacznijmy od jednostek. W przypadku temperatury funkcja zwraca wynik w stopniach Celsjusza, co przeważnie nam wystarcza. Czasem przydatne są wyniki w kelwinach – wystarczy wtedy dodać 273,15, aby przeliczyć wartość ze stopni Celsjusza na kelwiny.

Z ciśnieniem sytuacja jest nieco bardziej skomplikowana. W obowiązującym nas układzie jednostek SI ciśnienie reprezentowane jest w paskalach (Pa). Jednak 1 Pa to mała wartość, więc często używane są kilopaskale (1 kPa = 1000 Pa), a nawet megapaskale (1 MPa = 1 000 000 Pa). Aby jeszcze było „trudniej”, prognoza pogody (oraz nasz czujnik) zwraca wyniki w hektopaskalach (1 hPa = 100 Pa).

Ciśnienie atmosferyczne zależy od wielu czynników, a w szczególności od miejsca wykonywania pomiaru – im wyżej położony jest punkt pomiaru, tym niższe jest ciśnienie. Wysoko w górach ciśnienie jest niższe niż nad morzem. Jako odniesienie przyjmuje się uśrednione ciśnienie atmosferyczne na poziomie morza o wartości 1013,25 hPa. W wielu „gotowcach” można znaleźć stałą o takiej wartości – warto więc wiedzieć, skąd się to bierze.

Wzór barometryczny

Wykres ciśnienia względem wysokości nad poziomem morza wygląda tak jak poniżej. Warto z tego zapamiętać chociaż tyle, że ciśnienie maleje wraz ze wzrostem wysokości, a konkretnie na każde 100 metrów wzrostu wysokości ciśnienie spada o mniej więcej 11,7 hPa – za chwilę udowodnimy, że to całkiem dokładne przybliżenie, które może nam sporo ułatwić.

Zależność ciśnienia od wysokości

Zależność ciśnienia od wysokości

Ciśnienie bezwzględne w naszej lokalizacji możemy obliczyć za pomocą wzoru barometrycznego, który oprócz kilku stałych fizycznych wykorzystuje również informacje o ciśnieniu względnym w punkcie odniesienia, o naszej wysokości oraz o temperaturze powietrza (właśnie w kelwinach).

Wzór barometryczny

Wzór barometryczny

Ten wzór jest tutaj bardzo ważny. Warto zatem przyjrzeć mu się bliżej – mamy trzy stałe fizyczne, które na pewno świetnie znają wszyscy studenci, więc przypominamy je tylko dla formalności:

  • g – przyspieszenie ziemskie (9,80665 m/s2)
  • µ – masa molowa powietrza (0,0289644 kg/mol)
  • R – stała gazowa (8,31446261815324 J/molK)

Mamy też trzy zmienne:

  • h – wysokość wykonywania pomiaru
  • T – temperatura (w kelwinach)
  • p0 – ciśnienie atmosferyczne na poziomie odniesienia (względne)

Wniosek z tego jest taki, że znając ciśnienie na poziomie morza, wysokość oraz aktualną temperaturę, możemy policzyć ciśnienie panujące w danym miejscu. W związku z tym po przekształceniu wzoru na podstawie zmierzonego ciśnienia będziemy mogli obliczyć np. wysokość względem poziomu morza.

Ciśnienie względne, bezwzględne i prognoza pogody

Wstęp teoretyczny już prawie za nami, teraz jeszcze kilka nowych nazw. Ciśnienie panujące w danym punkcie nazywane jest ciśnieniem bezwzględnym (lub absolutnym). We wzorze barometrycznym powyżej jest to wartość p (czyli wynik obliczeń) – jest to wartość, którą zwraca nasz czujnik LPS25HB.

Natomiast ciśnienie względne jest to ciśnienie, jakie wskazywałby czujnik, gdyby wykonywał pomiar w danych warunkach pogodowych, ale na wysokości 0 metrów nad poziomem morza – wartość ta we wzorze barometrycznym jest opisana jako p0

Ciśnienie względne i bezwzględne są sobie równe, gdy jesteśmy na poziomie morza. Jeśli wykonujemy pomiar w innym miejscu (na innej wysokości), to możemy zmierzyć już tylko ciśnienie bezwzględne, a względne obliczymy w tej sytuacji właśnie za pomocą wzoru barometrycznego.

Teraz powinno być już jasne, dlaczego musieliśmy zawrzeć tak dużo teorii w tej części kursu. Wyniki odczytane z naszego czujnika wymagają odpowiednich przeliczeń, inaczej będą mało przydatne – teraz możemy sensownie wykorzystać zebrane informacje.

Przeliczanie ciśnienia na inne jednostki

W związku z tym, że znamy już teorię, możemy wrócić do praktyki. Zacznijmy od „zdobycia” ciśnienia wzorcowego – możemy je pobrać z aktualnej prognozy pogody. Informację na ten temat znajdziemy np. na stronie https://meteo.imgw.pl/ – wystarczy wpisać tam aktualną lokalizację.

Sprawdzanie aktualnego ciśnienia względnego

Sprawdzanie aktualnego ciśnienia względnego

Kolejna ważna informacja to położenie naszego punktu pomiarowego. Tutaj również odpowiednie dane są łatwe do wyszukania – pomocny będzie serwis https://www.wysokosciomierz.pl. Po kliknięciu w odpowiednie miejsca na mapie wyświetlona zostanie informacja o wysokości nad poziomem morza – w naszym przypadku było to 37 m n.p.m.

Teraz możemy obliczyć, ile w danej lokalizacji powinno wynosić ciśnienie bezwzględne. W tym celu do wcześniejszego wzoru barometrycznego podstawiamy stałe fizyczne oraz uzyskane przed chwilą dane (trzeba tylko pamiętać o podaniu temperatury w kelwinach). Czyli w naszym przypadku:

  • ciśnienie względne: 1020,5 hPa
  • temperatura: 18,3*C = 18,3 + 273,15 = 291,45 K
  • wysokość nad poziomem morza: 37 m
Wykorzystanie wzoru barometrycznego

Wykorzystanie wzoru barometrycznego

Wynik uzyskany za pomocą tego długiego wzoru to 1016,1 hPa. Nieco mniej dokładny wynik da się uzyskać też o wiele prościej. Wspominaliśmy, że ciśnienie spada o mniej więcej 11,7 hPa na każde 100 metrów. Z prostej proporcji można więc wyliczyć, że na wysokości 37 m n.p.m. ciśnienie powinno być niższe o (37 * 11,7) / 100 = 4,33, czyli powinno wynosić 1020,5 − 4,33 = 1015,67 hPa. Jak widać, ta prosta metoda daje całkiem dobre wyniki.

Pomiar ciśnienia względnego

Teraz już wiemy, jakiej wartości ciśnienia powinniśmy oczekiwać w naszej lokalizacji. Wracamy do kodu, uruchamiamy go i sprawdzamy wskazania ciśnienia. W naszym przypadku było to 1022,2 hPa zamiast spodziewanych 1016,1 hPa.

Ciśnienie zmierzone przez czujnik LPS25HB

Ciśnienie zmierzone przez czujnik LPS25HB

Łatwo policzyć, że nasz błąd to 6,1 hPa, czyli sporo. Błąd ten wynika z kilku czynników. Po pierwsze, do obliczeń wykorzystaliśmy pomiar ciśnienia z internetowej stacji pogodowej, której czujniki znajdują się w tym samym mieście co my, ale realnie są jednak w innej lokalizacji. Po drugie, wykorzystaliśmy mało precyzyjną informację na temat naszej wysokości względem poziomu morza – zakładając, że nie robimy tego eksperymentu, leżąc brzuchem na środku parkingu, tylko znajdujemy się w budynku na jakimś piętrze (lub w piwnicy), nasza rzeczywista wysokość względem poziomu morza jest inna.

Po trzecie, czujnik LPS25HB to dość skomplikowany układ, który powinien zostać skalibrowany po tym, gdy został przylutowany do modułu. Jest on kalibrowany przez producenta podczas produkcji, ale wysoka temperatura sprawia, że konieczna jest ponowna kalibracja (więcej informacji na ten temat znaleźć można w dokumentacji).

Możemy teraz sami dokonać kalibracji, ale najpierw przekształćmy wzór. Nasz czujnik zwraca wartość ciśnienia bezwzględnego oraz temperaturę. Możemy więc przekształcić wzór barometryczny, tak aby obliczyć ciśnienie względne.

Przekształcenie wzoru barometrycznego

Przekształcenie wzoru barometrycznego

Ręczne obliczenia takich wartości są mało pasjonujące. Mamy jednak wydajny mikrokontroler STM32L4 z koprocesorem arytmetycznym, możemy więc przepisać kod w taki sposób, aby układ zwracał nam od razu samodzielnie ciśnienie względne. 

Najpierw musimy dodać bibliotekę math.h do naszego projektu:

Następnie w pętli głównej programu dodajemy niezbędne przeliczenia:

Teraz program będzie wyświetlał ciśnienie względne, czyli takie, które możemy od razu porównywać z prognozą pogody (dla przypomnienia: było to 1020,5 hPa):

Informacja o ciśnieniu względnym wyliczona na podstawie pomiaru z czujnika

Informacja o ciśnieniu względnym wyliczona na podstawie pomiaru z czujnika

Kalibracja czujnika

Nasze wyniki obarczone są sporym błędem. Uzyskaliśmy 1026,47 hPa zamiast 1020,5 hPa – możemy jednak dokonać teraz kalibracji czujnika. W dokumentacji znajdziemy informację o tym, że czujnik jest kalibrowany podczas produkcji, a później konieczna jest kalibracja po lutowaniu.

Wśród rejestrów układu LPS25HB znajdziemy dwa odpowiedzialne za kalibrację: RPDS_L oraz RPDS_H. Razem tworzą one 16-bitową wartość, która będzie korygowała odczyty z czujnika. 

Rejestry służące do kalibracji czujnika LPS25HB

Rejestry służące do kalibracji czujnika LPS25HB

Zacznijmy od dodania do pliku lps25hb.h deklaracji nowej funkcji:

Teraz przechodzimy do pliku lps25hb.c i dodajemy definicje rejestrów RPDS_L oraz RPDS_H:

Dodajemy również funkcję lps25hb_set_calib:

Na koniec do funkcji main dodajemy wywołanie lps25hb_set_calib wraz z parametrem kalibracyjnym (po funkcji inicjalizującej działanie czujnika). Powinien on odpowiadać błędowi pomiaru pomnożonemu przez 16, czyli jeśli błąd wynosił tym razem 1026,47 − 1020,5 = 5,97, to wywołujemy ją z parametrem 96 (zaokrąglony wynik 16 * 5,97).

Oczywiście wartość użyta jako parametr zależy od konkretnego czujnika, więc każdy powinien podać obliczoną samemu wartość. Teraz wyniki powinny być już wystarczająco bliskie wartości wzorcowej:

Pomiar ciśnienia względnego za pomocą skalibrowanego czujnika LPS25HB

Pomiar ciśnienia względnego za pomocą skalibrowanego czujnika LPS25HB

Wartość kalibracyjna będzie inna dla każdego czujnika. Jej przechowywanie w kodzie programu jest prostym, ale nie najlepszym rozwiązaniem – o wiele lepiej byłoby wartość kalibracyjną zapisywać w pamięci EEPROM, którą poznaliśmy w poprzedniej części kursu. Dzięki temu osoba korzystająca z naszego urządzenia mogłaby samodzielnie kalibrować czujnik (np. za pomocą odpowiedniej opcji w menu na wyświetlaczu graficznym).

Pomiar wysokości bezwzględnej

Umiemy już zmierzyć ciśnienie bezwzględne, wiemy, jak przeliczyć je na wartość względną i wykonać kalibrację czujnika. Możemy więc przystąpić do budowania własnej stacji pogodowej albo wykorzystać nasz czujnik do nieco innego celu.

Do tej pory używaliśmy wzoru barometrycznego do obliczania ciśnienia. Teraz wykorzystamy wzór w przekształconej formie – wszystko po to, aby znając temperaturę oraz ciśnienie, obliczyć wysokość, na jakiej dokonujemy pomiaru. Wzór po przekształceniu będzie wyglądał następująco:

Przekształcenie wzoru barometrycznego do wyznaczenia wysokości

Przekształcenie wzoru barometrycznego do wyznaczenia wysokości

Parametr T to jak zwykle temperatura w kelwinach, p to ciśnienie absolutne zmierzone przez czujnik, a p0 to ciśnienie w punkcie odniesienia. Wiele przykładowych programów dostępnych w sieci jako p0 przyjmuje ciśnienie średnie na poziomie morza, czyli 1013,25 hPa. Zacznijmy od napisania programu, który będzie działał właśnie w ten sposób.

Inicjalizacja i kalibracja czujnika pozostają bez zmian, a pętla główna wygląda teraz następująco:

Po uruchomieniu program pokazuje następujący rezultat (Twoje wyniki mogą być zupełnie inne):

Pomiar wysokości nad poziomem morza przy ciśnieniu na poziomie odniesienia jako 1013,25 hPa

Pomiar wysokości nad poziomem morza przy ciśnieniu na poziomie odniesienia jako 1013,25 hPa

Błąd może być duży, a będzie on wynikał on z użycia wartości średniej. Według prognozy pogody ciśnienie w naszej lokalizacji wynosi 1020,5 hPa, spróbujmy więc użyć go jako parametru p0:

Tym razem wynik powinien być już zbliżony do prawdziwego. W naszym przypadku wysokość odczytana z mapy to 37 m, a program wskazywał około 45 m – różnica wynika głównie z tego, że znajdowaliśmy się w budynku.

Pomiar wysokości względem poziomu morza za pomocą czujnika LPS25HB

Pomiar wysokości względem poziomu morza za pomocą czujnika LPS25HB

Jak widać, pomiar wysokości wymaga ustawienia ciśnienia względnego odpowiednio do panujących warunków atmosferycznych. To dlatego wysokościomierze ciśnieniowe używane np. w lotnictwie wymagają dokładnych danych pogodowych. Dopiero wiedząc, jakie ciśnienie panuje w znanym punkcie (np. na najbliższym lotnisku), można poprawnie obliczyć wysokość.

Jednym z zadań lotnisk jest dostarczanie dokładnej prognozy pogody

Jednym z zadań lotnisk jest dostarczanie dokładnej prognozy pogody

Pomiar wysokości względnej

Barometru możemy jednak użyć również do mierzenia wysokości względnej (np. względem biurka lub punktu, w którym go włączyliśmy). Dzięki temu, jeśli podłączymy układ do baterii i zabierzemy go w plecaku na wycieczkę rowerową, to będziemy mogli na bieżąco śledzić różnice wysokości.

Wystarczy, że w programie najpierw damy czujnikowi nieco więcej czasu na stabilizację. Dotychczas czekaliśmy tylko 100 ms, teraz poczekamy aż 5 s (za chwilę ten dużo dłuższy czas będzie ważny). Następnie wykonamy pomiar ciśnienia wzorcowego, a w pętli będziemy obliczać różnice.

Po uruchomieniu zobaczymy efekt podobny do poniższego. Układ działa – jeśli go podniesiemy, widoczna będzie zmiana położenia, ale odczyty będą niestabilne. Niestety, nawet jeśli czujnik będzie znajdował się w tym samym położeniu, to otrzymywane wyniki będą „skakać”.

Niestabilny pomiar wysokości względnej

Niestabilny pomiar wysokości względnej

Uśrednianie wyników z czujnika LPS25HB

Prostą metodą na zwiększenie precyzji pomiarów jest wykonanie ich wielokrotnie i obliczenie średniej. Czujnik LPS25HB ma możliwość automatycznego uśredniania wyników z 32 pomiarów. W obecnej konfiguracji czujnik wykonuje 25 pomiarów na sekundę, więc jeśli włączymy uśrednianie 32 wyników, to czas jego reakcji będzie wynosił nieco ponad sekundę, ale wyniki powinny być o wiele stabilniejsze.

Przechodzimy do pliku lps25hb.c oraz dodajemy definicję rejestru FIFO_CTRL:

Czujnik posiada wbudowany bufor na 32 wyniki zorganizowany właśnie jako kolejka FIFO. Dotychczas go nie używaliśmy, ale teraz nam się przyda. Najpierw musimy włączyć tę kolejkę – wykonamy to przez prostą zmianę w rejestrze CTRL_REG2.

Opis rejestru CTRL_REG2 z dokumentacji czujnika LPS25HB

Opis rejestru CTRL_REG2 z dokumentacji czujnika LPS25HB

Ustawienie bitu numer 6 (czyli wpisanie tam jedynki) sprawi, że kolejka FIFO zostanie uruchomiona. Będziemy jeszcze musieli ją skonfigurować w rejestrze FIFO_CTRL.

Konfiguracja kolejki za pomocą rejestru FIFO_CTRL

Konfiguracja kolejki za pomocą rejestru FIFO_CTRL

Kolejka może działać w różnych trybach – nas interesuje FIFO Mean mode, czyli użycie bufora do obliczania średniej wartości. Pola bit WTM_POINT pozwalają na wybór liczby próbek używanych do obliczania średniej – nas interesują 32 próbki.

Konfiguracja liczby próbek wykorzystywanych do liczenia średniej

Konfiguracja liczby próbek wykorzystywanych do liczenia średniej

Zapis do rejestrów CTRL_REG2 oraz FIFO_CTRL dodajemy do funkcji lps25hb_init:

Teraz już powinno być jasne, dlaczego potrzebowaliśmy więcej czasu przed odczytem ciśnienia, które ma być później używane jako punkt odniesienia. Po prostu musimy poczekać, aż kolejka FIFO się zapełni. Co prawda w praktyce zajmuje to nieco ponad 1 s, ale lepiej dać czujnikowi więcej czasu na ustabilizowanie działania.

Uśrednienie wyników sprawia, że odczyty są znacznie stabilniejsze

Uśrednienie wyników sprawia, że odczyty są znacznie stabilniejsze

Układ powinien działać teraz znacznie lepiej – podniesienie płytki nawet o kilkadziesiąt centymetrów powinno dać widoczne efekty.

Reakcja czujnika na podnoszenie i opuszczanie

Reakcja czujnika na podnoszenie i opuszczanie

Nasz czujnik raczej nie sprawdzi się jako precyzyjny wysokościomierz lotniczy (takie precyzyjne czujniki są znacznie droższe i bardziej skomplikowane), ale na pewno jest to ciekawy przykład na to, jak można wykorzystać pozornie nudny pomiar ciśnienia.

Zadanie domowe

  1. Popraw kod z ćwiczeń w taki sposób, aby odczytywanie 24-bitowej wartości działało poprawnie również, gdy wartość zwracana przez czujnik byłaby ujemna.
  2. Dodaj do programu z ostatniego ćwiczenia obsługę diody RGB, której kolor powinien wskazywać różnicę wysokości (np. wartości ujemne kolor niebieski, wartości dodanie kolor czerwony). Wykorzystaj PWM do płynnej zmiany kolorów.
  3. Zapoznaj się z notą katalogową czujnika LPS25HB i zobacz jakie inne możliwości ma ten układ. Zwróć szczególną uwagę na rejestry REF_P_XL, REF_P_L i REF_P_H – postaraj się wykorzystać je w taki sposób, aby czujnik od razu zwracał różnicę ciśnienia względem punktu startowego.

Podsumowanie – co powinieneś zapamiętać?

Gotowe funkcje z biblioteki HAL sprawiają, że komunikacja za pomocą I2C jest bardzo prosta. Możemy skupić się więc na analizie not katalogowych używanych czujników, aby nawiązać z nimi komunikację. Warto też zapamiętać teorię na temat pomiarów ciśnienia – oczywiście nie trzeba znać na pamięć wzoru barometrycznego (wraz ze stałymi), ale przynajmniej warto wiedzieć, czego szukać.

Czy wpis był pomocny? Oceń go:

Średnia ocena 5 / 5. Głosów łącznie: 30

Nikt jeszcze nie głosował, bądź pierwszy!

Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!

W kolejnej części kursu wrócimy do tematu liczników (timerów). Wykorzystamy je do obsługi popularnych wyświetlaczy 7-segmentowych oraz czujnika ultradźwiękowego. Przy okazji wrócimy też na chwilę do analogowych peryferii mikrokontrolera i uruchomimy wzmacniacz.

Nawigacja kursu

Główny autor kursu: Piotr Bugalski
Współautor: Damian Szymański, ilustracje: Piotr Adamczyk
Oficjalnym partnerem tego kursu jest firma STMicroelectronics
Zakaz kopiowania treści kursów oraz grafik bez zgody FORBOT.pl

barometr, ciśnienie, i2c, kurs, kursSTM32L4, stm32l4

Trwa ładowanie komentarzy...