4. Przykładowy program #1: Skaner adresów I2C
Kurs Arduino #20: I2C w zastosowaniach
Najprostszym program, który pomoże nam na samym początku jest skaner adresów I2C. Pozwala on na zwrócenie – jak sama nazwa wskazuje- głównego adresu urządzenia. Z pomocą tego prostego programu jesteśmy w stanie zweryfikować poprawność podłączenia układu, jak i sam fakt czy urządzenie działa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
#include <Wire.h> //importujemy bibliotekę odpowiedzialną za uruchomienie I2C byte blad, adres; //definiujemy zmienne blad i adres jako byte (0-255) int urzadzenia; //definiujemy zmienną urzadzenia jako int void setup() { Wire.begin(); //uruchamiamy I2C Serial.begin(9600); //uruchamiamy UART Serial.println("Skaner kodow I2C"); //tytuł programu Serial.println("KURS I2C FERIAR-LABORATORY"); Serial.println("========================"); } void loop() { Serial.println("Skanuje..."); urzadzenia = 0; //ilosc znalezionych urządzeń na początku wykonywania programu //jest równa zero for(adres = 1; adres < 127; adres++ ) //wykonaj funkcję od 1 do 127 co odpowiada ilości bitów //bo 7 bit = > 7^2 = 127 { Wire.beginTransmission(adres); //wyslij do urządzenia slave aktualną wartość zmiennej adres blad = Wire.endTransmission(); //przypisz zmiennej blad, odczyt danych switch (blad) { case 0: //jak funkcja endTransmission zwróci wartość 0 Serial.print("* Znaleziono urzadzenie o adresie 0x"); if (adres<16) //jezeli adres jest mniejszy od 16 { Serial.print("0"); //wstaw 0 } Serial.println(adres,HEX); //wydrukuj wartość zmniennej adres, pod którą wykryto urządzenie //wartość zmiennej jest równoznaczna z adresem urządzenia urzadzenia++; //inkrementuj zmienną urządzenia o 1 break; case 4: //jak funkcja endTransmission zwróci wartość 4 Serial.print("Nieznany błąd przy adresie 0x"); if (adres<16) { Serial.print("0"); } Serial.println(adres,HEX); break; } } if (urzadzenia == 0) //jezeli wartosc zmienniej urzadzenia jest 0 { Serial.println("Nie znaleziono zadnych urzadzen I2C"); } Serial.println("==================================="); delay(5000); //odczekaj 5 sekund } |
Program jest stosunkowo prosty, ponieważ wszystko „kręci” się wokół zwracanych wartości przez funkcję endTransmission(). Na samym początku importujemy bibliotekę odpowiedzialną za I2C, oraz definiujemy trzy zmienne:
- blad– do tej zmienne będą zapisywane zwracane wartości z funkcji endTransmission()
- adres– tutaj będziemy zapisywać aktualnie badany adres
- urzadzenia– ta zmienna będzie zawierać ilość znalezionych urządzeń
Następnie w funkcji początkowej uruchamiamy magistralę I2C oraz UART. Teraz w pętli głównej programu ustalmy początkową wartość urządzeń, czyli 0, a następnie przy pomocy funkcji for badamy czy jakieś urządzenie znajduje się pod adresem z zakresu 1 do 127 (bo 7bit, czyli 7 do drugiej potęgi daje nam 127). W tym celu po I2C wysyłana jest liczba o 1 większa od poprzedniej, zawartej w zmiennej adres. W międzyczasie przypisujemy do zmiennej blad funkcję endTransmission(), do której będą zapisywane zwracane wartości.
Następnie przy pomocy funkcji warunkowej switch…case, sprawdzamy jaka wartość została przypisana zmiennej blad.
Jeżeli wartość naszej zmiennej wynosi 0 to znaczy, że urządzenie slave zwróciło bit Ack, w związku z czym wystawi o nim informację „* Znaleziono urzadzenie o adresie 0x”, ale jeżeli adres jest mniejszy od 16 to wstawi przed ten adres jeszcze „0”, a na końcu sam adres, w postaci heksadecymalnej oraz inkrementuje wartość zmiennej urzadzania o 1. Natomiast jeżeli funkcja blad zwróci wartość 4 to znaczy że wystąpił jakiś błąd przy tym adresie, tzn. jakieś urządzenie może znajduje, ale jest problem z połączeniem, transmisją itp.
Na samym końcu programu -poza funkcją for- sprawdzane jest czy jakieś urządzenie zostało w ogóle wykryte. Jeżeli wartość zmiennej urzadzenia jest równa 0 to wtedy program wystawia informację na monitor portu szeregowego, że nie znaleziono żadnych urządzeń w tym zakresie adresów.
5. Przykładowy program #2: Obsługa układu MPU-6050 przy pomocy I2C
Jako podsumowanie tej części poradnika, stworzymy program obsługujący układ MPU-6050, przy pomocy I2C korzystając wyłącznie z rejestrów, a nie z gotowych bibliotek.
Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <Wire.h> #define MPU_ADRES 0x68 //główny adres układu MPU-6050 #define TEMP_OUT_H 0x41 //adres rejestru wewnętrznego odpowiadającego za odczyt temperatury (bajt wysoki) int16_t xGyro, yGyro, zGyro, Temp; //16bitowe zmienne typu int, do których zostanie przypisane wartości //żyroskopu oraz temperatury void setup() { Wire.begin(); //uruchamiam I2C Serial.begin(9600); //uruchamiam UART Wire.beginTransmission(MPU_ADRES); //rozpocznij transmisję do urządzenia z adresem 0x68 Wire.write(0x6B); //wyślij adres 0x6B, odpowiadający za uruchomienie modułu Wire.write(0); //obudź układ Wire.endTransmission(); //zakończ transmisję } void loop() { Wire.beginTransmission(MPU_ADRES); //rozpocznij transmisję do urządzenia z adresem 0x68 Wire.write(TEMP_OUT_H); //wyślij adres 0x41 odpowiadający wysokiemu bajtowi temperatury Wire.endTransmission(); //zakończ transmisję Wire.requestFrom(MPU_ADRES,8); //zażądaj od urządzenia o adresie 0x68 zwrócenia 8 bajtów Temp= Wire.read()<<8|Wire.read(); //przesuń bajt o 8 bitów w lewo i dodaj do tej zmiennej następny bajt xGyro= Wire.read()<<8|Wire.read(); //przesuń bajt o 8 bitów w lewo i dodaj do tej zmiennej następny bajt yGyro= Wire.read()<<8|Wire.read(); //przesuń bajt o 8 bitów w lewo i dodaj do tej zmiennej następny bajt zGyro= Wire.read()<<8|Wire.read(); //przesuń bajt o 8 bitów w lewo i dodaj do tej zmiennej następny bajt Serial.print("Temperatura= "); //wypisz wartość temperatury Serial.print(Temp/340.00+36.53); //wzor podany w nocie katalogowej układu Serial.print(" *C"); Serial.print(" | X Gyro= "); //wypisz wartość dla osi X żyroskopu Serial.print(xGyro); Serial.print(" | Y Gyro= "); //wypisz wartość dla osi Y żyroskopu Serial.print(yGyro); Serial.print(" | Z Gyro= "); //wypisz wartość dla osi Z żyroskopu Serial.println(zGyro); delay(400); //odczekaj 400ms przed następnym pomiarem } |
Na samym początku definiujemy adresy urządzenia oraz rejestru wewnętrznego, następnie tworzymy 16bitowe zmienne typu int, którym w dalszej części programu przypiszemy odczyty z żyroskopu oraz temperatury.
W pętli startowej programu uruchamiamy I2C oraz UART. Następnie rozpoczynamy transmisję adresu, oraz dwóch specjalnych adresów uruchamiających moduł
Na samym końcu zamykamy transmisję. Teraz nasz moduł jest uruchomiony i gotowy do działania, dzięki czemu możemy przejść do pętli głównej programu.
W pętli głównej, znowu odwołujemy się do adresu głównego naszego układu, a następnie wysyłamy adres rejestru wewnętrznego odpowiedzialnego za temperaturę i zamykamy transmisję. Następnie informujemy Arduino, że ma się spodziewać 8 bajtów danych.
Bajty:
- 1-2 bajt wysoki i niski odczytanej temperatury
- 3-4 bajt wysoki i niski odczytanej wartości żyroskopu dla osi X
- 5-6 bajt wysoki i niski odczytanej wartości żyroskopu dla osi Y
- 7-8 bajt wysoki i niski odczytanej wartości żyroskopu dla osi Z
Następnie wykonamy dwa działania:
- przesunięcia bitowego w lewo
- dodania bajtów
Żeby było łatwiej wytłumaczyć to stworzyliśmy taką animację:
W pierwszej kolejności pobieramy pierwszy bajt z buforu, jakim jest w tym przypadku Temp_Out_H i „umieszczamy” go w zmiennej. Następnie „przesuwamy” go w lewo o 8 bitów, oraz pobieramy następny bajt z buforu (Temp_Out_L) i umieszczamy go w zmiennej. W taki sposób otrzymujemy wartość naszej temperatury.
Kiedy pobierzemy bajt z buforu to jest on usuwany, przez co nie ma problemów z odczytywaniem bajtów w poprawnej kolejności. Gdy mamy tą informację to możemy pobierać kolejny bajty z buforu i formować je w odpowiednie informacje. W naszym przypadku są to wartości żyroskopu dla osi X,Y,Z. Pamiętaj, że to co prezentuję będzie działać tylko i wyłącznie dla tego modułu. Sposób kontroli bitów jest przystosowany dla tego układu, ponieważ jak zerkniemy w notę katalogową tego układu:
to widzimy, że adresy odpowiedzialne za temperaturę i żyroskop X,Y,Z są ułożone po kolei. Gdybyś np. chciał odczytać wartości akcelerometru i żyroskopu, pomijając za razem temperaturę to najpierw byś musiał odwołać się adresu 0x3B i odczytać 6 bajtów, a następnie do adresu 0x43 i oczytać następne 6 bajtów.
Ale wracajmy do programu. Gdy już przebrnęliśmy przez odebranie danych z układu to teraz możemy, je wyświetlić na monitorze portu szeregowego. W przypadku temperatury to musimy jeszcze uformować odpowiednio otrzymane dane według wzoru z noty katalogowej.
W ten sposób dobrnęliśmy do końca naszego przykładu :)
Po wgraniu programu powinniśmy otrzymać taki efekt:
6. Podsumowanie
Po tej części kursu Arduino powinieneś wiedzieć:
- Co to jest I2C
- Na jakiej zasadzie działa
- Podstawowe funkcje oraz ich implementacja w prostych programach
Staraliśmy się przedstawić zagadnienia w sposób przejrzysty i zrozumiały i mamy nadzieję, że od teraz obsługa magistrali I2C nie powinna sprawiać Ci większego problemu :)
Materiały do tej części kursu:
Jeżeli chcesz być informowany na bieżąco o nowych częściach kursu to kliknij ?Lubię to!? bądź subskrybuj naszą stronę, aby otrzymywać na adres e-mail nowości ze strony. Jeżeli masz jakieś pytania to śmiało zadawaj je na forum ; )