Kurs STM32 F1 HAL – #12 – I2C w praktyce, akcelerometr

Kurs STM32 F1 HAL – #12 – I2C w praktyce, akcelerometr

Na zakończenie kursu STM32 omówimy bardziej rozbudowany przykład. Na warsztat weźmiemy moduł z układem LSM303D, który zawiera m.in. akcelerometr oraz magnetometr.

Zobaczymy, jak można odczytywać z niego dane za pomocą I2C oraz jak zaprezentować wyniki na wyświetlaczu graficznym.

Podstawowe informacje o LSM303D

Na początek musimy poznać nasz czujnik. Jest on nieco bardziej skomplikowany, niż omawiane wcześniej. Na szczęście dostępna jest do niego dobra dokumentacja. W naszym przykładzie nie omówimy wszystkich możliwości układu LSM303D. Skupimy się jednak na realizacji możliwie prostego i uniwersalnego mechanizmu odczytu danych z tego czujnika.

Zestaw elementów do kursu

Gwarancja pomocy na forum Błyskawiczna wysyłka

Zestaw ponad 120 elementów do przeprowadzenia wszystkich ćwiczeń z kursu można nabyć u naszych dystrybutorów! Dostępne są wersje z płytką Nucleo lub bez niej!

Kup w Botland.com.pl

 Najważniejsze materiały to:

  • datasheet - w nim znajdziemy pełny opis możliwości układu, protokół komunikacji itd.
  • schemat modułu - jak zwykle obraz jest wart więcej niż tysiąc słów. Warto zwrócić uwagę na sposób podłączenia układu oraz obecne rezystory podciągające

Przy okazji ważna uwaga. Omawiamy czujnik LSM303D, jednak można się zetknąć z układami o podobnej nazwie, ale innym działaniu. Są to starsze modele LSM303DLM i LSM303DLHC - pomimo bardzo podobnej nazwy, ich sposób działania jest inny. Układy te nie są już produkowane. Istnieje nikła szansa, że się na nie natkniemy, ale sporo dostępnych przykładów i bibliotek przeznaczonych jest dla starszych modeli, które mogę nie działać z nowym LSM303D.

Moduł LSM303D wykorzystywany w tej części. Źródło: POLOLU - producent.

Moduł LSM303D wykorzystywany w tej części. Źródło: POLOLU - producent.

LSM303D - komunikacja z modułem

W poprzedniej części kursu omówiliśmy sposób komunikacji poprzez I2C. Teraz możemy wykorzystać zdobytą wiedzę oraz napisany już program do rozpoczęcia pracy z nowym układem. Czytając datasheet zobaczymy, że komunikacja jest bardzo podobna do poznanej poprzednio.

Poprzednio używaliśmy układu 24AA01, który posiadał adres 0xa0. Nowy układ może mieć jeden z dwóch adresów 0x3c lub 0x3a, w zależności od stanu wyprowadzenia SA0. Oglądając schemat modułu zobaczymy, że na płytce znajduje się rezystor podciągający, co oznacza, że linia SA0 ma domyślnie stan wysoki, a adres układu to 0x3a. Na schemacie zobaczymy również, że linie SDA i SCL posiadają już rezystory podciągające. Możemy więc zrezygnować z podłączania własnych rezystorów podciągających.

Teraz możemy podłączyć moduł zgodnie z rysunkiem:

i2c-lsm303_bb

STM32 - schemat podłączenia modułu z LSM303D.

Gdy mamy podłączony układ, czas zająć się programem. Wykorzystamy kod napisany poprzednio, dostosowując go do nowego układu.

Po pierwsze musimy zmienić adres układu. Poprzednio było to 0xa0, teraz zdefiniujemy stałą, w której zapiszemy adres układu. Ułatwi to ewentualną modyfikację adresu w przyszłości:

Poprzednio wywoływaliśmy HAL_I2C_Mem_Read oraz HAL_I2C_Mem_Write, ale znaczna liczba parametrów oraz konieczność rzutowania typu na uint8_t nie jest zbyt wygodna. Dlatego w nowym programie napiszemy własne funkcje, które nieco ułatwią nam pracę. Na początek napiszemy funkcję odczytującą wartość z rejestru układu LSM303D. Schemat komunikacji z układem jest taki sam jak w przypadku pamięci eeprom, więc chociaż to nie pamięć również wywołamy HAL_I2C_Mem_Read.

Kod wygląda następująco:

Pełna lista oraz opis dostępnych rejestrów znajduje się w dokumentacji LSM303D. Poniżej jej fragment:

LSM_01

Fragment dokumentacji układu LSM303D.

Zaczniemy od sprawdzenia, czy nasz układ w ogóle działa. Jak widzimy, dostępny jest rejestr WHO_AM_I (adres 0x0f). Jest to rejestr tylko do odczytu, który zawsze powinien zwracać wartość 01001001b, czyli 0x49.

Odczytamy teraz jego zawartość, sprawdzimy czy jest poprawna i wyślemy przez UART odpowiedni komunikat. Program, który to realizuje wygląda następująco:

Komunikację przez UART oraz przekierowanie printf już znamy, więc program nie powinien być trudny do przeanalizowania:

W efekcie powinniśmy zobaczyć następujący komunikat w okienku terminala portu szeregowego:

LSM_02

Efekt programu sprawdzającego działanie modułu z LSM303D.

Natomiast układ na płytce stykowej wyglądał tak:

STM32 - pierwsze próby z modułem LSM303D.

STM32 - pierwsze próby z modułem LSM303D.

Odczyt temperatury

Zanim przejdziemy dalej, napiszemy kilka funkcji pomocniczych oraz uporządkujemy nieco program. Napisaliśmy już funkcję do odczytu rejestru, więc teraz napiszemy jej odpowiednik do zapisu:

Dodatkowo czytając dokumentację, zauważymy, że odczytywane wartości np. temperatury, przyspieszenia, czy strumienia magnetycznego są wartościami 16-bitowymi ze znakiem (zapisanymi w kodzie U2).

Możemy również napisać prostą funkcję, która będzie odczytywała taką wartość:

Ta sztuczka z reg | 0x80 oznacza włączenie auto-inkrementacji. Dzięki temu gdy odczytujemy dwa bajty, zamiast dwa razy to samo otrzymujemy wartości z kolejnych rejestrów. Wszystko jest opisane w dokumentacji LSM303D (ale łatwo o tym zapomnieć).

Teraz czas trochę posprzątać - podobnie jak w części dotyczącej wyświetlacza, przeniesiemy funkcje sterujące układem LSM303D do osobnych plików. Dodamy również stałe, definiujące rejestry układu. Będziemy mogli wykorzystywać je w programie zamiast "magicznych liczb".

Plik nagłówkowy lsm303d.h wygląda następująco:

Natomiast plik z kodem lsm303d.c:

Teraz możemy wrócić do programu głównego. Musimy odpowiednio skonfigurować LSM303D, czyli włączyć pomiar temperatury. W dokumentacji znajdujemy odpowiedni rejestr, czyli CTRL5.

LSM_03

Fragment dokumentacji LSM303D dotyczący pomiaru temperatury.

Jak widzimy, domyślnie pomiar temperatury jest wyłączony (pole TEMP_EN ma wartość 0). Zapiszemy więc do CTRL5 odpowiednią wartość

Przy okazji ustawiliśmy częstotliwość odczytów z magnetometru na 50Hz - nie wykorzystamy tego w tym momencie, ale skoro jest w tym samym rejestrze, to dlaczego nie ustawić jej "na zapas".

Po włączeniu pomiaru, musimy dać układowi trochę czasu, dodajemy więc małe opóźnienie. Odczyt temperatury jest już prosty - wystarczy wczytywać wartość z rejestrów TEMP_OUT_L i TEMP_OUT_H.

Napisaliśmy funkcję lsm_read_value, więc oba rejestry odczytamy jednocześnie. Funkcja wykona za nas również konwersję na 16-bitową wartość ze znakiem. Kod wygląda następująco:

Pełny kod programu widzimy poniżej:

Rezultat działania widoczny w oknie terminala - powinna być to aktualna temperatura:

LSM_04

LSM303D - odczyt aktualnej temperatury.

Akcelerometr w praktyce na STM32

Poznaliśmy już trochę układ LSM303D, czas przejść do głównego tematu tej części kursu, czyli odczytu danych z akcelerometru. W tym celu musimy nieco zmienić konfigurację układu (włączyć akcelerometr) oraz odczytać dane.

Opis konfiguracji jak zwykle znajdziemy w nocie katalogowej. Tym razem interesuje nas rejestr CTRL1. Trzy najwyższe bity określają prędkość odczytywania danych (ustawimy 25Hz jako przykład). Natomiast trzy najniższe bity odpowiadają za uruchomienie odczytów (z osi Z, Y i X):

LSM_05

Fragment dokumentacji LSM303D dotyczący akcelerometru.

Konfigurację wykonamy instrukcją:

Teraz możemy, podobnie jak w przypadku temperatury pobierać i wyświetlać dane:

W programie znajdziemy nieco więcej kodu, w tym zakomentowane instrukcje printf - omówimy je za chwilę. Cały program wygląda następująco:

Efekt działania powinien wyglądać mniej więcej jak poniżej:

LSM_06

Efekt działania programu LSM303D - odczyt z akcelerometru.

Wyniki mogą być nieco zaskakujące, jeśli nie zajmowaliśmy się wcześniej akcelerometrami. Spróbujmy więc zrozumieć co oznaczają. Odczyty są kodowane 16-bitowo ze znakiem. Więc ich zakres wynosi od -32768 do 32767. Czytając dokumentację, odkryjemy, że akcelerometr LSM303D może działać w kilku zakresach pomiarowych: 2 g, 4 g, 8 g i 12 g. Domyślna jest konfiguracja 2 g, do niej odnoszą się wyniki.

Ponieważ przyspieszenie 2 g, to pełny zakres, odpowiada mu odczyt 32767. Minimalna wartość wynosi -2 g, i daje wynik -32768. Możemy więc przeliczyć otrzymane wyniki na znane z fizyki wartości przyspieszenia:

W kodzie programu wystarczy odkomentować drugi printf (najlepiej zakomentowując pierwszy - inaczej wyniki będą nieco nieczytelne). Otrzymamy teraz następujący rezultat:

LSM_07

Przeliczone wartości odczytane z LSM303D.

Mając wyniki w takiej postaci, łatwiej jest zrozumieć ich znaczenie. Jak widzimy, wzdłuż osi X i Y mamy niewielkie odczyty, a na oś Z działa przyspieszenie prawie 1 g. Wynik może być pewnym zaskoczeniem, przecież nasza płytka leży spokojnie na biurku, nie przyspiesza w żadnym kierunku.

Mamy wiec poprawny wynik - na oś Z działa przyspieszenie ziemskie, a na X i Y - też trochę. Gdyby czujnik idealnie wypoziomować, powinniśmy otrzymać przyspieszenie zerowe na X i Y oraz 1 g dla osi Z. Niestety, nawet wtedy otrzymany wynik mógłby się zmienić np. po zmianie temperatury. Dlatego czujnik powinien być kalibrowany.

Co więcej chcąc obliczać np. przesunięcie robota, musimy pamiętać, że 1 g pochodzące od przyspieszenia ziemskiego i tak pojawi się w otrzymywanych wynikach.

STM32 i LSM303D - Projekt prostej poziomicy

Warto sprawdzić jak będą zmieniały się odczyty przechylając płytkę z czujnikiem. Na module z LSM303D znajdziemy nadruk z oznaczeniem kierunków osi X, Y oraz Z:

lsm303d-3-osiowy-cyfrowy-akcelerometr-magnetometr-i2cspi-modul-pololu

Wykorzystywany moduł, źródło zdjęcia: strona producenta Pololu.

Spróbujemy wykorzystać nasz czujnik do określania nachylenia czujnika - zbudujemy więc prostą, cyfrową poziomicę. Na początek uprościmy sobie trochę zadanie i zamiast w 3D, będziemy rozwiązywać problem w dwóch wymiarach.

Spróbujmy napisać program, który wyświetli kąt pod jakim nasz czujnik jest ułożony względem płaszczyzny ziemi. Z pomocą przychodzi nam trygonometria. Funkcja arcus tangens pozwala na przeliczenie uzyskanych wyników:

Ponieważ wyniki są w radianach, przeliczamy je na stopnie. Dodajemy też 90, aby uzyskać kąt między płytką, a płaszczyzną ziemi. Efekt wygląda jak na poniższym screenie:

LSM_08

LSM303D - odczyt kąta płytki względem płaszczyzny ziemi.

Do prezentowania wyników wykorzystamy wyświetlacz LCD, który omówiliśmy wcześniej. Mamy więc przygotowane odpowiednie procedury - wystarczy skopiować pliki omówione wcześniej.

Układ podłączamy zgodnie z rysunkiem:

i2c-lsm-lcd_bb

Kurs STM32 - schemat podłączenia modułu LSM303D i wyświetlacza graficznego.

Jednocześnie będziemy wykorzystywać komunikację przez SPI, I2C oraz UART. W tym celu kopiujemy następujące moduły z poprzednich przykładów (pliki .h oraz .c):

  • lsm303d,
  • lcd,
  • bitmap,
  • font.

Mając odpowiednie biblioteki, możemy przystąpić do pisania programu. W programie wyświetlimy poziomą linię - która będzie zachowywała kierunek nawet podczas obracania płytki z czujnikiem i wyświetlaczem.

Do poprzedniego programu dodajemy kod inicjalizujący wyświetlacz - był omówiony wcześniej. Teraz omówimy tylko pętlę główną programu:

Odczytujemy przyspieszenie działające wzdłuż osi X oraz Y (układ dużo lepiej działa w pionie, dlatego zamiast osi Z tym razem używana jest oś Y).

Mając wektor przyspieszenie ziemskiego, łatwo możemy obliczyć współrzędne odcinka do niego prostopadłego. W tym celu zamieniamy miejscami współrzędne x i y oraz przeliczamy zakres odczytów akcelerometru. Pełne wyprowadzenie matematyczne nie jest trudne - wystarczą podstawy geometrii, które pominiemy już w kursie.

Kod programu:

Działanie programu w praktyce widoczne jest na poniższym filmie:

Podsumowanie

W tej części kursu poznaliśmy podstawy działania czujnika LSM303D. Sprawdziliśmy sposób komunikacji z układem. Wykorzystaliśmy wbudowany termometr oraz akcelerometr. Nie zajęliśmy się natomiast magnetometrem. Zadanie to pozostaje dla chętnych, jako zadanie domowe.

Nawigacja kursu

W kolejnym odcinku zajmiemy się małym podsumowaniem całego kursu. Poruszymy również temat kontynuacji serii związanej z programowaniem STM32.

Autor kursu: Piotr Bugalski
Testy: Piotr Adamczyk
Redakcja: Damian Szymański

akcelerometr, kurs, kursSTM32F1HAL, programowanie, stm32, żyroskop

Załączniki

Komentarze

Komentarze do tego wpisu są dostępne na forum: