Pora na trzeci artykuł z cyklu o Jetson TK1, którego tematem są interfejsy komunikacyjne. Zaczniemy od prostej kontroli stanu pinów GPIO. Następnie przejdziemy przez interfejs UART, a zakończymy na magistrali I2C.
Poznanie tych interfejsów w praktyce jest ważne, a nawet niezbędne, jeśli chcemy wykorzystać opisywaną platformę przy budowie robotów.
Piny ogólnego przeznaczenia mogą być kontrolowane poprzez mechanizm sysfs systemu Linux. Sysfs jest to wirtualny system plików zapewniony przez jądro linuxa, który umożliwia korzystanie z niskopoziomowych urządzeń oraz interfejsów z poziomu przestrzeni użytkownika.
Zaczniemy od prostego wykorzystania tego mechanizmu: z poziomu powłoki tekstowej ustawimy logiczną jedynkę na pinie PGIO_PH1 (pin numer 57). Nie będę tutaj opisywał całej funkcjonalności mechanizmu sysfs. Opowiem o podstawowych mechanizmach, które mogą być przydatne w robotyce. Na początku musimy przejść w tryb superużytkownika:
ubuntu@tegra-ubuntu:~$ sudo su
Zbliżenie złącza oraz sygnałów IO (po lewej GND / pin 2, zaś po prawej PGIO_PH1 / pin 50).
Tak jak już zostało wspomniane sysfs działa z wykorzystaniem wirtualnych plików, więc przejdziemy do odpowiedniej lokalizacji i poprosimy jądro Linuxa o to, by wyeksportowało kontrolę nad pinem numer 57 do przestrzeni użytkownika, czyli:
root@tegra-ubuntu:/home/ubuntu# cd /sys/class/gpio
root@tegra-ubuntu:/sys/class/gpio# echo 57 > export
Możemy zauważyć, że w danej lokalizacji pojawił się folder o nazwie gpio57:
root@tegra-ubuntu:/sys/class/gpio# ls
export gpio143 gpio57 gpiochip0 gpiochip1016 unexport
Teraz poinformujemy komputer o kierunku działania naszego pinu poprzez wpisanie in lub out do pliku direction oraz ustawimy stan wysoki na tym pinie:
root@tegra-ubuntu:/sys/class/gpio# cd gpio57
root@tegra-ubuntu:/sys/class/gpio/gpio57# echo out > direction
root@tegra-ubuntu:/sys/class/gpio/gpio57# echo 1 > value
Zdjęcie odczytu napięcia na GPIO_PH1 / pin 50.
Możemy podejrzeć aktualne wartości poprzez wyświetlenie pliku /sys/kernel/debug/gpio:
root@tegra-ubuntu:/sys/class/gpio/gpio57# cat /sys/kernel/debug/gpio
GPIOs 0-255, platform/6000d000.gpio, tegra-gpio:
gpio-57 (sysfs ) out hi
gpio-58 (vdd-lcd-bl-en ) out lo
gpio-59 (panel rst ) out lo
gpio-63 (avdd-hdmi-pll ) out hi
gpio-70 (temp_alert ) in hi
gpio-82 (raydium-irq ) in lo
gpio-84 (raydium-reset ) out lo
gpio-86 (vdd-hdmi ) out hi
gpio-108 (usb0-vbus ) in lo
gpio-109 (usb1-usb2-vbus ) in hi
gpio-111 (hdmi_hpd ) in lo
gpio-122 (vdd-lcd-bl ) out lo
gpio-128 (Power ) in hi
gpio-132 (sdhci_wp ) in hi
gpio-136 (sdmmc-en-supply ) out lo
gpio-138 (reg-dcdc-1v2 ) out hi
gpio-143 (headphone detect ) in hi
gpio-170 (sdhci_cd ) in hi
GPIOs 1016-1023, platform/as3722-pinctrl, as3722-gpio, can sleep:
gpio-1018 (as3722-gpio2-supply ) out hi
gpio-1020 (as3722-gpio4-supply ) out lo
Aby mieć pewność możemy zmierzyć miernikiem - na pinie 57 mamy napięcie około 1.8V (jest to napięcie zasilania procesora Tegra K1). Następnie, aby udowodnić, że to my mamy kontrolę nad tym pinem ustawiamy stan niski i ponownie mierzymy napięcie:
root@tegra-ubuntu:/sys/class/gpio/gpio57# echo 0 > value
root@tegra-ubuntu:/sys/class/gpio/gpio57# cat /sys/kernel/debug/gpio
GPIOs 0-255, platform/6000d000.gpio, tegra-gpio:
gpio-57 (sysfs ) out lo
gpio-58 (vdd-lcd-bl-en ) out lo
gpio-59 (panel rst ) out lo
gpio-63 (avdd-hdmi-pll ) out hi
gpio-70 (temp_alert ) in hi
gpio-82 (raydium-irq ) in lo
gpio-84 (raydium-reset ) out lo
gpio-86 (vdd-hdmi ) out hi
gpio-108 (usb0-vbus ) in lo
gpio-109 (usb1-usb2-vbus ) in hi
gpio-111 (hdmi_hpd ) in lo
gpio-122 (vdd-lcd-bl ) out lo
gpio-128 (Power ) in hi
gpio-132 (sdhci_wp ) in hi
gpio-136 (sdmmc-en-supply ) out lo
gpio-138 (reg-dcdc-1v2 ) out hi
gpio-143 (headphone detect ) in hi
gpio-170 (sdhci_cd ) in hi
GPIOs 1016-1023, platform/as3722-pinctrl, as3722-gpio, can sleep:
gpio-1018 (as3722-gpio2-supply ) out hi
gpio-1020 (as3722-gpio4-supply ) out lo
Zdjęcie odczytu napięcia na GPIO_PH1 / pin 50.
Aby zakończyć pracę w tym trybie należy posprzątać po sobie, czyli oddać kontrolę nad pinem z powrotem dla jądra linuxa:
root@tegra-ubuntu:/sys/class/gpio/gpio57# cd ..
root@tegra-ubuntu:/sys/class/gpio# echo 57 > unexport
root@tegra-ubuntu:/sys/class/gpio/gpio57# exit
Szybkie i łatwe! Mechanizm sysfs umożliwia zaawansowaną kontrolę wejść/wyjść ogólnego przeznaczenia, ale jest to odpowiednio bardziej skomplikowane i nie będzie w pełni opisane w tym artykule.
Warto zauważyć, że w internecie jest wiele gotowych bibliotek implementujących mechanizm sysfs w C++, dla przykładu biblioteka libgpio. Aby ją zainstalować potrzebne są następujące kroki:
ubuntu@tegra-ubuntu:~$ git clone https://github.com/Linutronix/libgpio.git
ubuntu@tegra-ubuntu:~$ cd libgpio
ubuntu@tegra-ubuntu:~/libgpio$ ./autogen.sh
ubuntu@tegra-ubuntu:~/libgpio$ ./configure
ubuntu@tegra-ubuntu:~/libgpio$ make && sudo make install
Jak już mamy zainstalowaną bibliotekę możemy skorzystać z programiku napisanego w C.
ubuntu@tegra-ubuntu:~$ git clone https://github.com/dkoguciuk/jetson_gpio.git
ubuntu@tegra-ubuntu:~$ cd jetson_gpio
ubuntu@tegra-ubuntu:~/jetson_gpio$ mkdir build && cd build
ubuntu@tegra-ubuntu:~/jetson_gpio/build$ cmake ../ && make
ubuntu@tegra-ubuntu:~/jetson_gpio/build$ sudo ./jetson_gpio
Program ma za zadanie wczytać znak z klawiatury i w zależności od tego czy będzie to "1" czy "0" ustawić odpowiedni stan na pinie numer 57 oraz wyjść jeżeli będzie to znak "q". Można go rozbudowywać, ale to już zależy od konkretnego zastosowania i kreatywności programisty.
Komunikacja UART z Jetson TK1
W procesorze TK1 dostępne są 4 niezależne instancje interfejsu UART (ciekawskich zapraszam do schematu Jetson TK1):
UART1 - złącze J3A2 (piny 41 oraz 44), rejestrowany w systemie jako /dev/ttyTHS0
UART2 - złącze J3A2 (piny 65 oraz 68), rejestrowany w systemie jako /dev/ttyTHS1
UART3 - brak wyprowadzeń, rejestrowany w systemie jako /dev/ttyTHS2
UART4 - wyprowadzony w postaci interfejsu RS232 na złącze J1A2 (typu dsub DB9), rejestrowany w systemie jako /dev/ttyS0
Ważną sprawą przy wykorzystaniu UARTu w Jetsonie są poziomy napięć.
Jak już wcześniej wspomniano procesor TK1 zasilany jest napięciem 1.8V, dlatego możemy mieć problemy przy próbie połączenia z urządzeniem zasilanym innym napięciem. Aby tego uniknąć należy wykorzystać prosty dwukierunkowy konwerter poziomów logicznych.
Jeżeli chodzi o wykorzystanie prawdziwych UARTów, to wszystko jest już praktycznie gotowe - nie trzeba nawet nadawać uprawnień do czytania i pisania plików ttyTHSx, ponieważ domyślnie użytkownik ubuntu należy do grupy dialout, która ma dostęp do tych plików. Nic tylko programować!
Zabawa interfejsem RS232 wyprowadzonym na UART4 może przysporzyć kłopotów.
Jest on rejestrowany w linuxie jako interfejs ttyS0, który jest wykorzystywany przez getty jako standardowa konsola poprzez mechanizm serwisów włączanych przy starcie systemu. Aby jednorazowo odzyskać kontrolę nad tym interfejsem należy zastopować odpowiedni serwis:
ubuntu@tegra-ubuntu:~$ sudo service ttyS0 stop
Aby permanentnie wyrzucić serwis z grona serwisów startowych wystarczy wykorzystać właściwy mechanizm, czyli stworzyć odpowiedni plik przykrywający ten standardowy:
ubuntu@tegra-ubuntu:~$ sudo sh -c 'echo manual >> /etc/init/ttyS0.override'
Teraz już można się cieszyć swobodnym dostępem do interfejsu RS232.
Wysokopoziomowe wykorzystanie interfejsu UART można przeprowadzić z wykorzystaniem mechanizmu termios wbudowanego w linuxa. Najpierw jednak połączymy sygnał UART_RX z UART_TX, by przetestować poprawność działania na przykładzie poniższego programu:
ubuntu@tegra-ubuntu:~$ git clone https://github.com/dkoguciuk/jetson_uart.git
ubuntu@tegra-ubuntu:~$ cd jetson_uart
ubuntu@tegra-ubuntu:~/jetson_uart$ mkdir build && cd build
ubuntu@tegra-ubuntu:~/jetson_uart/build$ cmake ../ && make
ubuntu@tegra-ubuntu:~/jetson_uart/build$ sudo ./jetson_uart
Program ma za zadanie wczytać znak z klawiatury i w zależności od tego czy będzie to "1" czy "0" wysłać go poprzez UART, a skoro sami połączyliśmy UART_TX oraz UART_RX, to od razu zgłosi się przerwanie zgłaszające gotowość danych. Jeżeli zostanie wysłany znak "q", to przy odebraniu go program wyczyści wszystkie zasoby i wyjdzie.
Po lewej UART2_RXD / pin 65, zaś po prawej UART2_TXD / pin 68.
Jetson TK1 - Interfejs I2C
W procesorze TK1 jest dostępnych sześć różnych szyn I2C. Z tego cztery są wyprowadzone na złącza J3A1 oraz J3A2. Należy jednak zwracać szczególną uwagę pod jaką nazwą rejestrowana jest konkretna szyna w jądrze Linuxa oraz na poziomy napięć konkretnych szyn.
Poniżej przedstawiono podstawowe informacje o wszystkich dostępnych dla robotyka interfejsach I2C w płytce deweloperskiej Jetson TK1.
Tabelka przedstawiająca wszystkie wyprowadzone interfejsy I2C.
Aby nie tracić czasu od razu zaczynamy zabawę z płytką od instalacji pakietu:
Gdzie ostatni parametr komendy oznacza skanowany numer szyny. Na powyższym listingu widać, że domyślnie na płytce do szyny 0 dołączone są 3 urządzenia i są nimi (ciekawscy mogą odnaleźć je na schemacie elektrycznym płytki):
kodek audio o adresie 0x1c,
sensor temperatury o adresie 0x4c,
pamięć EEPROM o adresie 0x56.
Oprócz tego domyślnie do szyny o numerze 4 dołączony jest również moduł zarządzania zasilaniem o adresie 0x40. Możemy teraz podejrzeć wartości wszystkich dostępnych rejestrów urządzenia za pomocą programu i2cdump:
Dobrze! Podstawy komunikacji I2C w Linuksie mamy za sobą.
Teraz czas na bardziej zaawansowane użycie tego interfejsu i pokażę to z wykorzystaniem modułu cyfrowego akcelerometru z wyjściem cyfrowym, którego sercem jest układ LIS35DE.
Układ jest zasilany napięciem od 2.16V do 3.6V, dlatego zachodzi konieczność użycia szyny GEN2_I2C, czyli i2c-1. Po podłączeniu sprawdzimy, czy układ jest wykrywany w Linuksie.
Jak widać moduł jest wykrywany pod adresem 0x1d. Mamy wszystko, by zrealizować cykliczne czytanie wartości przyspieszenia z czujnika. Można byłoby zrealizować to poprzez skrypt basha z wykorzystaniem wyżej przedstawionych narzędzi.
Jednak nie będziemy mieli wtedy pełnej kontroli nad częstotliwością odczytu. Z tego względu najlepiej jest wykorzystać wbudowane mechanizmy Linuksa do komunikacji z urządzeniami I2C.
Zbliżenie złącza oraz sygnałów IO (od lewej GND /pin 14, 3V3 / pin 16, GEN2_I2C_SCL / pin 18 oraz na końcu GEN2_I2C_SDA / pin 20).
W tym celu proponuję ściągnąć przygotowany przeze mnie program do odczytu wartości przyspieszenia z modułu KAmodMEMS2:
ubuntu@tegra-ubuntu:~$ git clone https://github.com/dkoguciuk/jetson_i2c.git
ubuntu@tegra-ubuntu:~$ cd jetson_i2c
ubuntu@tegra-ubuntu:~/jetson_i2c$ mkdir build && cd build
ubuntu@tegra-ubuntu:~/jetson_i2c/build$ cmake ../ && make
ubuntu@tegra-ubuntu:~/jetson_i2c/build$ sudo ./jetson_i2c
Program ma za zadanie połączyć się z płytką oraz cyklicznie, co 10 milisekund, odczytać i wyświetlić wartość przyspieszenia w osi x.
Obsługa interfejsu I2C wymaga jeszcze słowa komentarza odnośnie adresowania urządzeń I2C. Muszę przyznać, że z tymi adresami w Linuksie jest małe zamieszanie i dużo czasu spędziłem zanim zrozumiałem o co chodzi.
Programy wchodzące w skład paczki i2c-tools podają adres bez uwzględnienia najmniej ważnego bitu (LSb), który jest ustawiany na 0 lub 1 w zależności czy jest to akcja czytania, czy zapisania do urządzenia. Dlatego przeczytaliśmy wartość 0x1d.
Przy wykorzystywaniu niskopoziomowego mechanizmu opartego o mechanizm sysfs musimy się posługiwać pełnymi adresami docelowymi, czyli 0x3a przy zapisie oraz 0x3b przy odczycie wartości z danego rejestru.
Podsumowanie
Ktoś może zauważyć, że w tym wszystkim zabrakło miejsca dla SPI i muszę przyznać, że sam byłem zawiedziony, że Jetson nie ma łatwego dostępu do tego interfejsu. W chwili obecnej L4T ma domyślnie wyłączony moduł SPI. Aby go włączyć należałoby przekompilować jądro. Można znaleźć w sieci opis tej procedury, wydaje mi się jednak, że łatwiejszym rozwiązaniem będzie wykorzystanie urządzenia z interfejsem I2C.
To już koniec podstawowej wersji artykułu. Za pomocą przedstawionych mechanizmów można zintegrować porządnie działający system robotyczny, co postaram się pokazać w następnej, czwartej części. CUDAownego budowania robotów!
Autor kursu: Daniel (danioto) Koguciuk Redakcja: Damian (Treker) Szymański
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY na bazie Arduino i Raspberry Pi.
To nie koniec, sprawdź również
Przeczytaj powiązane artykuły oraz aktualnie popularne wpisy lub losuj inny artykuł »
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY z Arduino i RPi.
Trwa ładowanie komentarzy...