Biblioteka SMBUS w Pythonie do obsługi czujników w oparciu o i2c
Uruchomienie czujników zazwyczaj jest zadaniem bardziej wymagającym aniżeli podłączenie diody. Sprawa jeszcze bardziej się komplikuje gdy czujnik działa w oparciu o magistralę i2c.
Fizyczne podłączenie czujnika zawiera dwa elementy - podłączenie zasilania (czyli VCC i GND) oraz podłączenie dwóch pinów dedykowanych dla transmisji danych (SDA i SCL).
Gdy już podłączyliśmy czujnik i w lxterminal'u sprawdziliśmy przy pomocy komendy
i2cdetect -y 1
że czujnik jest widziany przez malinę (i pod jakim adresem), czas zabrać się za napisanie programu, który obsłuży komunikację pomiędzy maliną i czujnikiem. W tym celu posłużymy się biblioteką smbus (System Management Bus).
Czujniki działające na magistrali i2c to urządzenia o rozbudowanej komunikacji. Mam tu na myśli, że można od nich pobierać informacje, jak i wysyłać informacje (komendy). Oczywiście gdy chcemy zastosować czujnik do pomiaru konkretnej wielkości fizycznej interesuje nas przede wszystkim odczytanie tej wartości i na tym skupiamy naszą uwagę.
Często jest jednak tak że aby odczytać tę wartość musimy czujnik niajpierw uruchomić programowo. Dla przykładu aby czujnik zmierzył daną wartość wymaga on wzbudzenia poprzez podanie wartości logicznej 1 na konkretny numer jego rejestru. Bywa też tak, że czujnik może mieć konkretny adres rejestru, w którym w zależności czy podamy logiczne 0 czy 1, czujnik będzie realizował pomiar tylko jeden raz, po wywołaniu czujnika, albo w trybie ciągłym będzie rejestrował mierzoną wielkość.
No dobrze, a co z odczytem danych... Sprawa jest i prosta i skomplikowana zarazem. :) Jak już wyjaśniałem we wpisie o systemach zapisu danych jeden bajt jest w stanie określić liczbę od 0 do 255. A co jeżeli mierzona przez nas wielkość fizyczna jest większa niż 255 jednostek, np. ciśnienie atmosferyczne.
W takiej sytuacji czujnik mierząc daną wielkość zapisuje ją do kilku byte'ów po czym znając ich kolejność możemy odtworzyć liczbę w systemie binarnym, która liczy nie 8 cyfr, ale 16, 24 32 itd.
Takie szczególne rozwiązania opiszę już na przykładzie konkretnych czujników, natomiast wróćmy do naszej biblioteki. Poniżej opis komend jakie są dostępne w tej bibliotece:
write_quick(adres) - wysyła jeden bit do czujnika na miejsce bitu "read/write".
read_byte(adres) - odczytuje jeden bit z czujnika. Niektóre czujniki są tak proste, że taka komenda jest wystarczająca.
write_byte(adres,wartość) - wysyła jeden bit do czujnika. Jak w przypadku powyższej komendy dotyczy to prostych czujników bez rozbudowanego rejestru wewnętrznego.
read_byte_data(aders,rejestr) - odczytuje jeden byte ze wskazanego rejestru.
write_byte_data(aders,rejestr,wartość) - wysyła do czujnika "wartość" do wskazanego konkretnego rejestru.
read_word_data(aders,rejestr,wartość) - odczytuje wartość ze wskazanego rejestru, ale tym razem jest to wartość 16 bitowa.
write_word_data(aders,rejestr,wartość) - wysyła do czujnika "wartość" do wskazanego rejestru. Jest to odwrotność read_word_data.
process_call(adres,rejestr,wartość) - wysyła 16 bitową informację na wybrany rejestr (według Comm byte) i zwrotnie pobiera 16 bitów danych.
read_block_data(adres,rejestr) - odczytuje do 32 bitów danych ze wskazanego rejestru.
write_block_data(aders,rejestr,wartość) - wysyła do czujnika na wskazany rejestr do 32 bitów danych.
block_process_call(aders,rejestr,wartość) - wybiera rejestr (według Comm byte) wysyła do niego od 1 do 31 byte'ów i zwrotnie pobiera do 31 byte'ów.
W inecie udało mi się znaleźć dwie strony, które w miarę w przystępny sposób opisują specyfikę tej biblioteki.
- smbus 1
- smbus 2
Poniżej przykładowy kod wycięty z większego programu, który pokazuje jak wyciągnąć wartość określaną przez 16 bitów i przeliczyć to na wartość w postaci dziesiętnej, gdzie najstarszy bit oznacza znak wartości, czyli czy wartośćjest dodatnia czy ujemna.
import smbus
def read_byte(adr):
print(bus.read_byte_data(address, adr))
return bus.read_byte_data(address, adr) # komenda z SMBUS odczytująca 8 bitów ze wskazanego rejestru
def read_word(adr):
high = bus.read_byte_data(address, adr) # odpalamy funkcję do odczytu 8 bitów ze wskazanego rejestru.
low = bus.read_byte_data(address, adr+1) # zazwyczaj dwa bajty danych są zapisane w kolejnych po sobie rejestrach
val = (high <<8) + low #zapisujemy do jednej ziennej dwa bajty tak aby byte "high" przesunąć na początek 16 bitowej wartości
return val
def read_word_2c(adr): # funkcja przelicza wartośćbinarną na dziesiętną z uwzględnieniem że pierwszy bit oznacza czy wartość jest + czy -
val = read_word(adr)
if(val >= 0x8000):
return -((65535 - val) + 1)
else:
return val
bus = smbus.SMBus(1) # odpalamy komunikację
Jak powyższe zastosować?
Jako przykładem posłużymy się 3-osiowym żyroskopem i 3 - osiowym akcelerometrem w układzie MPU6050.
To co nas interesuje w temacie komunikowania się z czujnikiem po i2c to "mapa rejestrów". Producenci różnie ją publikują, czasem w podstwowej dokumentacji, a czesem tak jak w przypadku MPU6050 w oddzielnym dokumencie (o tutaj).
Na stronach od 6 do 8 jest cała lista rejestrów czujnika. Poniżej wycinek mapy rejestrów aby pokazać wam jak można ją odczytać.
Jak można zauważyć w pierwszej kolumnie mamy numer (adres) rejstru podany w systemie szesnastkowym. Druga kolumna to teżadres rejestru ale w systemie dziesiętnym. Trzecia kolumna opisuje co kryje sięw danym rejestrze. Czwarta kolumna informuje czy dane rejestr jest do odczytu czy do zapisu, czyli czy odczytujemy z niego wartośći czy do niego wpisujemy. Ostatnia kolumna opisuje budowę byte'u.
A teraz zaznaczony na rysunku kolorem czerwonym przykład. Jest to byte do odczytu, który zawiera starszy byte danych z akcelerometra w osi X. Ten konkretny rejestr ma nr 0x3b.
To powinno wystarczyć, przynajmniej jako dobry wstęp do komunikowania się w pythonie po magistrali i2c, z wykorzystaniem biblioteki smbus.
Pozdrawiam. :)