KursyPoradnikiInspirujące DIYForum

Kurs STM32 F4 – #9 – Obsługa I2C, akcelerometr

Kurs STM32 F4 – #9 – Obsługa I2C, akcelerometr

Podczas projektowaniu systemów elektronicznych bardzo często pojawia się potrzeba stosowania specjalistycznych czujników. Jeżeli nie są one wyposażone w wyjścia analogowe, to trzeba się z nimi porozumieć w inny sposób.

W tym odcinku kursu zapoznamy się z obsługą akcelerometru, który korzysta z I2C!

Interfejs I2C

I2C jest jednym z najpopularniejszych standardów komunikacyjnych występującym w świecie układów scalonych. Dzięki niemu jesteśmy w stanie przesyłać informacje cyfrowo.

I²C – szeregowa, dwukierunkowa magistrala służąca do przesyłania danych w urządzeniach elektronicznych. Została opracowana przez przedsiębiorstwo Philips na początku lat 80. Znana również pod akronimem IIC, którego ang. rozwinięcie Inter-Integrated Circuit oznacza „pośredniczący pomiędzy układami scalonymi”.

Wikipedia, 29.06.2016

Interfejsy cyfrowe - co zyskujemy?

Po pierwsze, dane przesyłane analogowo narażone są na zakłócenia. Urządzenie odbiorcze musi być wyposażone w przetwornik analogowo-cyfrowy, który będzie w stanie odczytać przesyłaną informację. W urządzeniach komunikujących się za pomocą interfejsów cyfrowych pomiary, filtracja i przekształcenia matematyczne odbywają się wewnątrz urządzenia, a my otrzymujemy gotowy wynik w postaci liczby.

Po drugie, każda informacja, którą chcemy przesyłać analogowo wymaga osobnej linii danych. Generuje to bardzo duże ograniczenie, związane z wielkością układu scalonego, ilością wyjść i miejscem na płytce. Wykorzystując interfejs cyfrowy ten problem znika prawie całkowicie (ogranicza nas jedynie prędkość transmisji danych).

Po trzecie, daje to możliwość przesyłania wielu różnych informacji nie tylko z czujnika do mikrokontrolera, ale również w drugą stronę.

Warstwa fizyczna I2C

I2C do komunikacji wykorzystuje tylko dwie linie transmisyjne.

  • SDA (Serial Data Line) - linia danych.
  • SCL (Serial Clock Line) - linia zegara.

Linie są dwukierunkowe, co umożliwia transmisję Half-Duplexową (obydwa urządzenia mogą nadawać i odbierać dane, ale nie jednocześnie). Linie są na stałe podciągnięte do źródła zasilania przez rezystory podciągające.

Sposób podłączenia magistrali I2C pomiędzy mikrokontrolerem a układem scalonym

Sposób podłączenia magistrali I2C pomiędzy mikrokontrolerem, a układem scalonym.

Dodatkową zaletą I2C jest możliwość podłączenia wielu urządzeń do magistrali. W opracowanym w latach 80 standardzie było to dokładnie 112 układów (7 bitowa przestrzeń adresowa pomniejszona o adresy zarezerwowane). W nowej wersji wartość ta wzrosła do nieco powyżej tysiąca urządzeń (10 bitowa przestrzeń adresowa).

Magistrala I2C.

Magistrala I2C.

Sposób działania I2C

W komunikacji I2C wyróżnia się dwa typy urządzeń - master i slave. Urządzenie master generuje sygnał zegarowy i inicjuje komunikację z urządzeniami slave, które odpowiadają na zapytania otrzymywane od mastera.

Protokół komunikacyjny

Istnieją cztery potencjalne tryby działania urządzeń podłączonych do magistrali I2C:

  1. master transmit - urządzenie master wysyła dane do urządzenia slave,
  2. master receive - urządzenie master odbiera dane nadawane przez urządzenie slave,
  3. slave transmit - urządzenie slave wysyła dane do urządzenia master,
  4. slave receive - urządzenie slave odbiera dane od nadawane przez urządzenie master.

Domyślnie master jest w trybie master transmit i tylko ten układ może rozpocząć komunikację.

Istnieją również 4 podstawowe typy transmisji:

  1. urządzenie master przesyłające jeden bajt do urządzenia slave,
  2. urządzenie master przesyłające wiele bajtów do urządzenia slave,
  3. urządzenie master odczytujące jeden bajt z urządzenia slave,
  4. urządzenie master odczytujące wiele bajtów z urządzenia slave.

Aby lepiej zrozumieć co będziemy potem implementować w programie, przyjrzyjmy się jak wygląda pierwszy z nich i jak wygląda podstawowa ramka danych w protokole I2C.

Master przesyłający jeden bajt do slave - schemat

Tabela z prostym schematem komunikacyjnym, kiedy urządzenie master przesyła jeden bajt do urządzenia slave. Tabela z dokumentacji układu LSM303.

Tabela z prostym schematem komunikacyjnym, kiedy urządzenie master przesyła jeden bajt do urządzenia slave. Tabela z dokumentacji układu LSM303.

  1. Transmisja rozpoczyna się od wysłania bitu startu przez urządzenie master (ST - Start).
  2. Następnie urządzenie master nadaje siedmiobitowy adres urządzenia, do którego ma trafić wiadomość (SAD - Slave Address)Na końcu adresu urządzenia (SAD) dodawany jest ósmy bit, który w zależności od swojego stanu komunikuje chęć dalszego nadawania bądź odbierania danych przez urządzenie master(+W - Write).
  3. Po odebraniu swojego adresu, urządzenie slave wysyła bit potwierdzenia (SAK - slave acknowledge).
  4. Następnie urządzenie master transmituje ośmiobitowy adres rejestru znajdującego się w urządzeniu slave, do którego mają trafić dane (SUB - sub-address).
  5. Po odebraniu adresu rejestru wewnętrznego, urządzenie slave ponownie potwierdza odbiór bitem SAK.
  6. W końcu urządzenie master transmituje bajt danych (DATA), który trafia do wcześniej sprecyzowanego rejestru (SUB).
  7. Ponowne potwierdzenie ze strony urządzenia slave.
  8. Urządzenia master zakańcza transmisję wysyłając bit stopu (SP - Stop).

Jak widzimy, schemat jest nieco bardziej skomplikowany niż w przypadku UARTa, lecz pozwala na wygodną komunikację pomiędzy pamięciami układów scalonych. Tyle teorii wystarczy nam, aby zrozumieć fundamentalne założenia interfejsu I2C. Pora na praktykę!

Gotowe zestawy do kursów Forbota

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

Zestaw elementów do przeprowadzenia wszystkich ćwiczeń z kursu STM32 F4 można nabyć u naszego dystrybutora! Zestaw zawiera m.in. płytkę Discovery, wyświetlacz OLED, joystick oraz enkoder.

Zamów w Botland.com.pl »

Akcelerometr

Akcelerometr jest urządzeniem mierzącym przyspieszenie liniowe wzdłuż konkretnej osi (lub wielu osi). W stanie spoczynku będzie on wskazywał przyspieszenie o wartości 1g wzdłuż osi Z, wynikające z przyspieszenia ziemskiego. Dzięki temu, za pomocą prostych przekształceń można określić orientację akcelerometru w przestrzeni względem wektora grawitacji.

Poza mierzeniem przyspieszenia ziemskiego, akcelerometr pozwala na wykrywanie wszelkich innych przyspieszeń, takich jak np. zmiany prędkości ruchu pojazdu, swobodny spadek, drgania.

Akcelerometrów używa się w zastosowaniach takich jak:

  • stabilizacja lotu obiektów latających takich jak samoloty, helikoptery, rakiety,
  • obracanie obrazu w telefonach i tabletach tak, aby zawsze był skierowany w górę,
  • wykrywanie drgań w obiektach mechanicznych, budynkach,
  • pomiar przyspieszenie pojazdów,
  • aktywności sportowe - chodzenie, bieganie, skakanie etc.

Układ scalony LSM303DLHC

Na dużej części płytek serii Discovery umieszczono układ scalony LSM303DLHC, zawierający w sobie 3 osiowy akcelerometr i 3 osiowy magnetometr. Płytka STM32F411DISCOVERY, na której pracujemy w ramach tego kursu również jest w niego wyposażona.

STM32F4_Discovery_9

Wbudowany akcelerometr z magnetometrem.

Najważniejsze parametry układu to:

  • pomiar przyspieszenia w zakresie ±2g/±4g/±8g/±16g,
  • pomiar pola magnetycznego w zakresie od ±1.3 do ±8.1 gaussa,
  • szesnastobitowy pomiar wartości,
  • dopuszczalne zasilanie w zakresie 2.16 - 3.6 V

Konfiguracja I2C w Cube

Krok 1. Tworzymy nowy projekt i konfigurujemy zegary jak było to omówione wcześniej.
Krok 2. Sprawdzamy w schemacie płytki Discovery, do których linii mikrokontrolera podłączony jest układ LSM303.

Schemat podłączenia układu LSM303DLHC na płytce STM32F411-Discovery

Schemat podłączenia układu LSM303DLHC na płytce STM32F411-Discovery.

Ze schematu wynika, że linia zegara SCL podłączona jest do wyprowadzenia PB6, a linia danych SDA do wyprowadzenia PB9.

Krok 3. Konfigurujemy linie PB6 i PB9 w odpowiednich trybach.

Konfiguracja wyprowadzenia PB6 jako linii zegara peryferium I2C1.

Konfiguracja wyprowadzenia PB6 jako linii zegara peryferium I2C1.

Konfiguracja wyprowadzenia PB9 jako linii danych peryferium I2C1

Konfiguracja wyprowadzenia PB9 jako linii danych peryferium I2C1.

Krok 4. Widzimy, że linie podświetlone są na żółto. Aby w pełni włączyć peryferium I2C1 należy to sprecyzować w panelu z lewej strony.

Aktywacja peryferium I2C2

Aktywacja peryferium I2C1.

Krok 5. Przechodzimy do panelu konfiguracyjnego I2C1.

Panel konfiguracyjny I2C1

Panel konfiguracyjny I2C1.

Sekcja Master Features zawiera parametry interfejsu I2C, które dotyczą sytuacji, w których nasze urządzenie działa w trybie master.

  • I2C Speed Mode - pozwala na wybranie jednego ze standardów określających częstotliwość linii zegarowej I2C. Dostępne prędkości to:
    • Standard Mode - oryginalna częstotliwość z początków lat 80, która wynosi 100 kHz
    • Fast Mode - 400kHz
    • High-speed mode - 3.4 MHz
    • Fast-mode plus (FM+) - 1MHz
    • Ultra Fast-mode (UFm) - 5MHz

Wszystkie peryferia I2C znajdujące się na wyposażeniu naszego mikrokontrolera mogą prowadzić transmisję tylko w standardzie Standard Mode oraz Fast Mode. Ponieważ układ LSM303DLHC również obsługuje Fast Mode, możemy sobie pozwolić na konfigurację właśnie tego trybu. Po wybraniu Fast Mode pojawi się nowy parametr, który należy pozostawić na wartości domyślnej.

  • I2C Clock Speed (Hz) - pokazuje obecnie ustawioną częstotliwość linii zegarowej.

Sekcja Slave features zupełnie nas nie interesuje, ponieważ mikrokontroler w przedstawionych tu zastosowaniach zawszę będzie urządzeniem typu master. Finalnie ustawione parametry powinny prezentować się następująco.

KursF4_9_9

Ostateczne ustawienia I2C.

Krok 6. Generujemy projekt pod nazwą 06_I2C.

Uruchomienie i odczytywanie danych z akcelerometru

Jesteśmy już gotowi do rozpoczęcia komunikacji z naszym urządzeniem. Musimy jednak wiedzieć co i w jaki sposób wysyłać, aby układ odpowiedział nam pożądanymi danymi. W tym celu zagłębimy się nieco w dokumentację układu LSM303.

Adresy urządzeń I2C

Pierwszą rzeczą jakiej potrzebujemy w każdej komunikacji I2C jest adres główny urządzenia (SAD). Na stronie 21 dokumentacji znajdziemy następujący fragment:

Fragment dokumentacji układu LSM303DLHC dotyczący adresu urządzenia w magistrali I2C

Fragment dokumentacji układu LSM303DLHC dotyczący adresu urządzenia w magistrali I2C.

Jak już wcześniej wspomniałem, adresy urządzeń są zazwyczaj 7 bitową liczbą, która w tym wypadku wynosi 0011001. W tabeli 14 widzimy, że aby zapisywać dane do modułu na końcu adresu należy dodać bit zerowy. W celu odczytywania danych ósmy bit musi być jedynką.

Oznacza to, że niezależnie od tego co sami tam wpiszemy, biblioteka sama ustawi odpowiednio ostatni bit zgodnie z wywołaną funkcją. My musimy tylko zadbać o to, żeby pierwsze 7 bitów zawierało poprawny adres. Jeżeli podamy do funkcji adres o wartości 00110011, zostanie on zamieniony na 0011001x i w miejsce x wstawiony zostanie odpowiednią wartość, zgodnie z wywołaną funkcją.

Problemy z adresowaniem w I2C

W tym miejscu pozwolę sobie poświęcić nieco miejsca na dywagację na temat adresów, ponieważ potrafią one sprawić niemało problemów. Większość z nich sprowadza się do przedstawionego scenariusza.

Z dokumentacji wyczytujemy, że adres naszego urządzenia to 0011001. Szybko wrzucamy tę liczbę do konwertera BIN->HEX (konwerter liczb binarnych na heksadecymalne) i otrzymujemy wartość 0x19. Umieszczamy ją w programie i próbujemy przesłać dane do urządzenia.

Dlaczego?

Pamiętajmy, że każda liczba przedstawiona w ten sposób w systemie ma 8 bitów. My podaliśmy tu tylko 7, dlatego musiało nastąpić niejawne dodanie jednego bitu z którejś ze stron. Na nasze nieszczęście zawsze będzie to dodanie zera z lewej strony, więc nasz adres zostaje odczytany jako 00011001, a więc w praktyce 0001100x. A przecież powinno to być 0011001x, gdzie x oznaczać będzie bit zapisu/odczytu!

Rozwiązanie:

Aby uniknąć nieporozumień z systemem, zawsze należy podać adres zapisany przez nas jawnie na 8 bitach. Możemy to zrobić na kilka sposobów. Najpopularniejszym z nich jest dokonanie przesunięcia bitowego w lewo na wartości, którą otrzymaliśmy po konwersji 7 bitowego adresu.

Taką właśnie linijkę wpisujemy do naszego programu (np. w sekcji private variables).

Uruchomienie akcelerometru

Następnie musimy sprawdzić czy akcelerometr jest domyślnie włączony, czy może trzeba go uruchomić programowo. Informacje na ten temat znajdziemy w opisie rejestru Control Register 1 na stronie 25 dokumentacji:

Specyfikacja rejestru Control Register 1 akcelerometru układu LSM303DLHC

Specyfikacja rejestru Control Register 1 akcelerometru układu LSM303DLHC.

Widzimy, że rejestr składa się z 3 głównych części:

  1. Bity [7:4] - ODR [3:0] - odpowiadają za parametr ODR (Octal Data Rate). Jest to po prostu częstotliwość dokonywania pomiarów. W tabeli 19 możemy znaleźć informację, że domyślna wartość to 0000. Sprawdzając co oznacza ta wartość w tabeli 20 dowiadujemy, się że domyślnie akcelerometr jest w trybie Power-down Mode, a więc domyślnie nie dokonuje pomiarów.
  2. Bit [3] - LPEN - pozwala na uruchomienie trybu niskiego poboru mocy. Domyślnie ten tryb jest wyłączony.
  3. Bity [2:0] - ZEN (Z axis enable), YEN, XEN - włączenie lub wyłączenie pomiarów na poszczególnych osiach. Domyślnie wszystkie osie są włączone.

Po krótkiej analizie przedstawionej wyżej strony dokumentacji wiemy już, że w celu uruchomienia dokonywania pomiarów trzeba wpisać odpowiednią wartość do rejestru CTRL_REG1_A. Dodajemy więc do naszego programu jego adres, aby później wiedzieć w które miejsce wpisywać dane.

Następnie musimy stworzyć maski bitowe odpowiadające konkretnym ustawieniom. Na początek chcemy odczytywać dane tylko z osi Z, z częstotliwością 100Hz. W tym celu potrzebne nam będą następujące maski.

Teraz potrzebujemy zmiennej, do której wpiszemy utworzoną przez nas konfigurację.

Znak "|" oznacza sumę bitową. Dzięki temu zmienna Settings powinna w wyniku zawierać wartość:

0000 0100 | 0101 0000 = 01010100.

Następnie musimy wpisać stworzone ustawienia do odpowiedniego rejestru. W tym celu posłuży nam funkcja HAL_I2C_Mem_Write.

Przyjmuje ona sześć parametrów. Przeanalizujmy po kolei każdy z nich.

  1. I2C_HandleTypeDef *hi2C - wskaźnik na strukturę konfiguracyjną I2C.
  2. uint16_t DevAddress - adres urządzenia, do którego mają zostać wysłane dane.
  3. uint16_t MemAddSize - rozmiar rejestru pamięci (w bajtach).
  4. uint8_t *pData - wskaźnik na pamięć, która zawiera dane do przesłania.
  5. uint16_t Size - liczba bajtów danych do przesłania.
  6. uint32_t Timeout - czas, który I2C może przeznaczyć na oczekiwanie na poprawne zakończenie komunikacji (w milisekundach).

W naszym wypadku poprawnie wypełniona funkcja będzie wyglądać następująco:

Tym sposobem uruchomiliśmy pomiary akcelerometru w osi Z z częstotliwością 100Hz.

Odczytywanie danych z akcelerometru

Teraz potrzebne są nam informacje dotyczące tego skąd możemy odczytać dane. Zaglądając do zestawienia wszystkich rejestrów układu LSM303DLHC na stronie 23 dokumentacji bardzo szybko odnajdziemy interesujące nas rejestry.

Zestawienie rejestrów układu LSM303DLHC z zaznaczonymi rejestrami danych osi Z

Zestawienie rejestrów układu LSM303DLHC z zaznaczonymi rejestrami danych osi Z.

Widzimy, że rejestrów danych jest aż 6, po dwa na każdą oś. Jest tak, ponieważ rejestry układu są ośmiobitowe, a dane z każdej osi zapisane są na 16 bitach. Litera L w nazwie rejestru oznacza Low, a więc młodszy (niższy) bajt całej wartości (mniej ważny). Litera H (High) oznacza bajt starszy (wyższy/ważniejszy).

Skąd biorą się określenia starszy czy ważniejszy? Przypomnijmy sobie jak zapisywana jest liczba szesnastobitowa, konkretnie typ uint16_t.

Sposób zapisu liczby szesnastobitowej bez znaku w komputerze

Sposób zapisu liczby szesnastobitowej bez znaku w komputerze.

Widzimy, że bajt starszy składa się z bitów o wyższych wartościach, a więc [15:8]. Na nim to zapisywana jest główna składowa wartości całkowitej. Ma więc on dużo większą wagę w kontekście całej liczby, dlatego jest "ważniejszy". Jeżeli liczba jest typu signed (ze znakiem - np. int16_t), najstarszy bit informuje o znaku ± całej liczby.

Na początek wystarczy nam odczyt jednego bajtu, a więc ważniejszej części danych. W tym celu potrzebujemy dwóch zmiennych. Do jednej będziemy wpisywać bezpośrednio odczytany bajt danych z akcelerometru. Druga będzie służyła do reprezentacji faktycznej wartości (po przesunięciu bitowym oraz zamianie na liczbę ze znakiem).

Do odczytu danych z akcelerometru użyjemy teraz funkcji HAL_I2C_Mem_Read. Budowa jest taka sama jak w przypadku funkcji Write, z jedną drobną różnicą. Tym razem parametr 4 to wskaźnik na obszar pamięci, do którego zostaną wpisane pobrane z urządzenia dane (a nie jak poprzednio, obszar z którego dane zostaną pobrane).

Wywołanie z odpowiednimi parametrami przedstawiono na poniższym listingu.

Ostatnią rzeczą którą musimy zrobić jest przepisanie pobranej wartości do zmiennej zawierającej wartość właściwą. Pamiętamy, że pobraliśmy starszy bajt danych, dlatego przy wpisywaniu do liczby typu int16_t musimy przesunąć go na odpowiednią pozycję, a więc 8 bitów w lewo.

Świetnie! Pełny kod powinien wyglądać jak na poniższych dwóch listingach.

Budujemy i wgrywamy stworzony program. Następnie otwieramy STMStudio i konfigurujemy podgląd zmiennej Zaxis.

Podgląd zmiennej Zaxis w programie STMStudio

Podgląd zmiennej Zaxis w programie STMStudio

Jeśli wszystko zrobiliśmy tak jak w przedstawionej instrukcji, powinniśmy mieć na ekranie aktualne odczytu z akcelerometru. Jeżeli płytka leży poziomo "mikrokontrolerem do góry", wartość zmiennej Zaxis powinna oscylować w okolicach 16000.

Interpretacja odczytów z akcelerometru

Obracając płytkę w rękach można zauważyć zmieniającą się wartość przyspieszenia. Dlaczego tak jest i co oznaczają te liczby? Aby odpowiedzieć na powyższe pytanie, musimy po raz kolejny sięgnąć do dokumentacji i zobaczyć gdzie w naszym układzie umieszczona jest oś Z. Odpowiednie informacje znajdziemy na stronie 17.

Umiejscowienie osi w układzie LSM303DLHC

Umiejscowienie osi akcelerometru w układzie LSM303DLHC.

Wiemy zatem, że oś Z skierowana jest prostopadle do płaszczyzny płytki i wartość dodatnią ma po stronie, na której znajduje się mikrokontroler. Co zatem oznacza odczytana z akcelerometru wartość? Żeby się tego dowiedzieć, musimy ponownie zajrzeć do dokumentacji i przywołać kilka wcześniej wspomnianych faktów.

Pierwszą informacją, której potrzebujemy, jest maksymalna wartość przyspieszenia, jaką jest w stanie zmierzyć akcelerometr. Zaglądając na stronę 27 dokumentacji możemy zobaczyć, że odpowiadają za to bity FS[1:0] rejestru CTRL_REG4_A.

Rejestr konfiguracyjny akcelerometru zawierający informacje o maksymalnej wartości mierzonego przyspieszenia

Rejestr konfiguracyjny akcelerometru zawierający informacje o maksymalnej wartości mierzonego przyspieszenia.

Domyślną wartością tego rejestru jest 00, co oznacza, że maksymalna wartość przyspieszenia, jaką możemy teraz zmierzyć to ±2g. Drugą informacją, która jest nam potrzebna do obliczeń, to maksymalna wartość, którą jest w stanie przyjąć zmienna przechowująca wartości pomiarów. Wiemy, że pomiar jest 16 bitowy i że jest ze znakiem. Oznacza to, że mierząc wartość przyspieszenia 2g, zmienna osiągnie swoją maksymalną wartość, a więc 32767.

Z tą wiedzą jesteśmy w stanie ułożyć równanie, przekształcające pobrane wartości na bardziej intuicyjną jednostkę z układu SI - (przyspieszenie ziemskie).

Dodajemy zatem linijkę dołączającą wspomnianą bibliitekę.

Następnie dodajemy zmienną, która będzie przechowywać wynik naszej operacji.

Na końcu wpisujemy równanie przekształcające zmierzoną wartość na jednostkę fizyczną.

Budujemy i wgrywamy program na mikrokontroler. W STMStudio importujemy do podglądu zmienną Zaxis_g. Zmieniamy typ wyświetlania danych na Bar Graph i zmieniamy wartości graniczne wykresu na -2.0 i 2.0 (zaznaczone na czerwono).

Podgląd zmiennej Zaxis_g w STMStudio

Podgląd zmiennej Zaxis_g w STMStudio

Żeby zmienić tytuł wykresu oraz tytuły poszczególnych osi (zaznaczone na niebiesko), wystarczy kliknąć na wykresie prawym przyciskiem myszy, wybrać Properties i zmodyfikować parametry.

Nasz wykres powinien teraz wizualizować mierzone przyspieszenie na wykresie słupkowym.

Odczytywanie większej liczby danych

Jak dotąd odczytywaliśmy tylko jeden (ważniejszy) bajt danych z akcelerometru. Pomiar akcelerometru jest jednak szesnastobitowy i warto to wykorzystać. Aby odczytać dwa bajty, musimy zmodyfikować program. Po pierwsze musimy zadeklarować zmienną Data jako tablicę.

Następnie przypominamy sobie, że parametr piąty funkcji HAL_I2C_Mem_Read (parametr size) oznacza liczbę bajtów, którą chcemy odczytać z urządzenia. Zaglądając do spisu rejestrów widzimy, że rejestry zawierające dane wyjściowe osi Z są umiejscowione w pamięci obok siebie.

Odczytując więc dwa bajty z rejestru  OUT_Z_L_A (adres w pamięci: 2C), zostanie odczytana jego wartość, a następnie nastąpi przejście do kolejnej komórki pamięci, a więc  OUT_Z_H_A (adres w pamięci: 2D) i stamtąd odczytany zostanie drugi bajt.

Wystarczy więc wywołać funkcję z odpowiednimi parametrami.

Dlaczego?

Jest to związane z drobnym niuansem, który łatwo przeoczyć przeglądając dokumentację. Znajduje się on na stronie 21.

Zapis w dokumentacji dotyczący autoinkrementacji adresów w układzie LSM303DLHC

Zapis w dokumentacji dotyczący autoinkrementacji adresów w układzie LSM303DLHC.

Mówi nam to tyle, że jeśli chcemy odczytać więcej niż jeden bajt danych, najstarszy bit adresu rejestru docelowego (SUB(7)) musi zostać ustawiony na 1. Zauważmy, że adresy rejestrów pamięci akcelerometru podane w dokumentacji również są zapisane na siedmiu bitach.

Jest to sytuacja zbliżona do tej opisanej przy okazji głównego adresowania, z tym że wtedy ustawienie odpowiedniego bitu odbywało się automatycznie i dotyczyło to bitu najmłodszego. W tym wypadku musimy ten bit ustawić sami i jest to bit najstarszy.

Wyposażeni w tę wiedzę definiujemy nową stałą zawierającą zmodyfikowany adres.

Teraz możemy już spokojnie wywołać funkcję odczytującą z właściwymi parametrami.

Ostatnia rzecz jaką musimy zmodyfikować, to połączenie obydwu bajtów danych podczas przekształcenia na liczbę typu int16_t. Ponieważ młodszy bajt danych odczytujemy jako pierwszy, pole Data[0] musimy po prostu przepisać do zmiennej bez modyfikacji. Starszy bajt będący w Data[1] przepisujemy tak jak poprzednio, z przesunięciem bitowym.

W całości nasz kod będzie się prezentował jak na poniższych listingach:

Określenie orientacji płytki na podstawie odczytów z akcelerometru

Ostatnią rzeczą, którą dziś zrobimy będzie wykorzystanie odczytów z akcelerometru w praktyce. Zadaniem będzie sterowanie diodami LED w sposób odzwierciedlający pomiary akcelerometru.

Krok 1. Konfigurujemy odpowiednie piny tak, aby można było sterować świeceniem diod LED.

Konfiguracja pinów w STMCubeMX

Konfiguracja pinów w STMCubeMX.

Krok 2. Modyfikujemy nasz kod tak, aby odczytywać dane z wszystkich trzech osi. W tym celu musimy włączyć pomiary z wszystkich trzech osi, a następnie odczytać 6 bajtów danych zaczynając od  pierwszego w kolejności rejestru  z danymi, a więc OUT_X_L_A.

W sekcji definicji dodajemy stałe przedstawione na poniższym listingu.

Krok 3. Następnie zwiększamy rozmiar tablicy Data i dodajemy dodatkowe zmienne do kolejnych osi (oczywiście moglibyśmy wszystko zrobić na tablicach, ale rozdzieliłem to w przykładzie dla zwiększenia czytelności).

Krok 4. Na końcu modyfikujemy ustawienia, które zostaną wpisane do akcelerometru, tak żeby uruchomić pomiary na wszystkich trzech osiach. Modyfikujemy odpowiednio parametry wywołania funkcji HAL_I2C_Mem_Read i dodajemy obliczenia do pozostałych osi.

Teraz w STMStudio możemy podglądać przyspieszenie w 3 osiach, a na diodach LED obserwować stworzone przez nas "sterowanie ruchem".

Podgląd przyspieszenia liniowego w 3 osiach w STMStudio

Podgląd przyspieszenia liniowego w 3 osiach w STMStudio.

Oczywiście zamiast sterowania zero-jedynkowego można by tu było zastosować sygnał PWM o wypełnieniu proporcjonalnym do przyspieszenia w danej osi. Wtedy jasność świecenia diod płynnie sygnalizowałaby aktualne przyspieszenie.

Podsumowanie

W tym odcinku nauczyliśmy się jak korzystać z jednej z podstawowych form komunikacji występującej między układami - interfejs I2C. Wiemy również jak uruchomić akcelerometr, jak odczytać z niego dane i jak je przedstawić za pomocą fizycznych jednostek. W załączniku znaleźć można oczywiście kody programów wykonywanych w tym artykule!

W następnym odcinku wykorzystamy kolejny interfejs - SPI. Dzięki niemu możliwe będzie wygodne używanie wyświetlacza OLED. Jeżeli macie jakieś pytania, sugestie lub coś jest dla was nie zrozumiałe, śmiało piszcie w komentarzach!

Nawigacja kursu

Autor kursu: Bartek (Popeye) Kurosz
Redakcja: Damian (Treker) Szymański

Załączniki

I2C (zip, 4 MB)

Projekt z artykułu, gotowy do zbudowania i wgrania na mikrokontroler

akcelerometr, i2c, kurs, kursSTM32F4, programowanie, stm32

Trwa ładowanie komentarzy...