Jako przykład tezy zawartej we wstępnie, posłużą nam moduły KAmodRPi PwrRELAY zawierający dwa przekaźniki elektromechaniczne oraz KAmodRPi ADC_DAC z przetwornikami A/C i C/A. Ich użycie znacznie ułatwia samodzielne eksperymentowanie z Odroidem.
KAmodRPi PwrRELAY – powtórka z GPIO
Pierwszy z modułów, KAmodRPi PwrRELAY (https://goo.gl/6rcPAM) wyposażono w dwa przekaźniki elektromechaniczne oraz dwa wyjścia tranzystorowe, którymi możemy sterować za pomocą linii GPIO. Można go zatem wykorzystać na przykład do sterowania domowymi urządzeniami elektrycznymi. W tym prostym przykładzie jeden z przekaźników posłuży do włączania i wyłączania lampki biurkowej za pośrednictwem terminala.
Na początku warto przyjrzeć się dokumentacji modułu, ponieważ znajdują się w niej m. in. informacje o sposobie jego podłączenia. Według schematu elektrycznego, przekaźniki są podłączone do linii 22 i 23 GPIO, które odpowiadają liniom 115 i 104 ODROIDa. Jeżeli poprawnie wykonaliśmy wszystkie kroki z poprzedniego artykułu, możemy je skonfigurować za pomocą terminala (bez uprawnień roota):
echo 115 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio115/direction
Ustawienie lub zerowanie GPIO wykonujemy poleceniami:
echo 1 > /sys/class/gpio/gpio115/value
echo 0 > /sys/class/gpio/gpio115/value
Oczywiście, do obsługi portu możemy wykorzystać również jedną z przykładowych aplikacji, omówionych w poprzednim artykule. O poprawnym działaniu modułu, a tym samym o stanie przekaźnika, poinformuje nas czerwony LED – świecący, gdy pin zostanie ustawiony. Mając działający i skonfigurowany moduł możemy zabrać się za ciąg dalszy, czyli podłączenie lampki do przekaźnika.
Każdy z przekaźników zamontowanych na module ma trzy styki: NC (Normally Closed), COM (Common), NO (Normally Open). Przy braku zasilania przekaźnika styki NC i COM są zwarte. Po ustawieniu wyjścia sterującego, zostaną zwarte styki COM i NO, co w module jest sygnalizowane świeceniem LED. Aby przyłączyć lampkę do przekaźnika, powinniśmy dołączyć ją do wejścia NO, natomiast do wejścia COM jeden z przewodów z gniazdka elektrycznego (lub odwrotnie). Dzięki temu, po odłączeniu zasilania obwód będzie rozwarty. Drugi przewód z gniazdka należy na stałe zewrzeć z drugim przewodem lampki. Teraz pozostaje przetestować zmontowany układ (fotografia 1).
Na koniec warto jeszcze przyjrzeć się pewnemu bardzo przydatnemu narzędziu obecnemu w systemie Linux. Jest nim cron i służy do cyklicznego wykonywania zadań. Jako przykład możemy wykonać cykliczne zapalanie i gaszenie światła.
Cron jest demonem systemu Linux, czyli programem uruchamianym przy starcie systemu i działającym w tle przez cały czas lub do momentu wyłączenia go przez użytkownika (do wyłączania demonów zwykle potrzebne są uprawnienia administratora). Program ten raz na minutę odczytuje plik zawierający listę zleconych mu zadań. Każdy użytkownik ma własną listę zadań, którą można wyświetlić poleceniem crontab -l.
Jeżeli wcześniej nie używaliśmy crona, plik ten może nie istnieć. Do jego tworzenia i edycji służy jedno polecenie: crontab -e. Powoduje ono otwarcie listy zadań w domyślnym edytorze tekstu. W ramach ćwiczenia skonfigurujemy crona, aby zmieniał stan wyjścia pinu raz na minutę. W tym celu wykorzystamy jeden z programów z poprzedniego artykułu: gpio_set_vlue. W oknie edytora dodajmy na samym końcu dwa wiersze:
1-59/2 * * * * /home/odroid/gpio_set_value 115 1
0-58/2 * * * * /home/odroid/gpio_set_value 115 0
Wpis spowoduje wywołanie w minutach parzystych (0-58/2) programu /home/pi/gpio_set_value z argumentami 115 1, natomiast w nieparzystych (1-58/2) tego samego programu z argumentami 115 0. Warto zwrócić uwagę na to, że musimy podać pełną ścieżkę do programu, który chcemy wykonać. Po zapisaniu pliku i wyjściu z edytora powinniśmy obserwować zmianę stanu przekaźnika co 1 minutę. Aby przerwać wykonywanie zadań, wystarczy usunąć wpis lub usunąć cały plik za pomocą cron -r. Należy także pamiętać, że do poprawnego działania niezbędne jest wcześniejsze wyeksportowanie pinu 115 i skonfigurowanie go jako wyjście. Jednym ze sposobów, aby działo się to automatycznie przy starcie systemu, jest użycie menedżera urządzeń udev.
Podobnie jak ostatnio, do pliku /etc/udev/rules.d/90-gpio.rules dodamy reguły:
SUBSYSTEM==”gpio”,KERNEL==”gpiochip0”,RUN+=”/bin/sh -c ‚echo 115 > /sys/class/gpio/export’”
SUBSYSTEM==”gpio”,KERNEL==”gpio115”,RUN+=”/bin/sh -c ‚echo out > /sys%p/direction’”
Pierwsza z nich eksportuje w sterowniku GPIO pin 115, natomiast druga konfiguruje go jako wyjście.
Do poprawnego działania crona niezbędne jest jeszcze ustawienie czasu w systemie. Jeżeli nasz ODROID jest przyłączony do Internetu, zrobi to dla nas ntpdate, który powinien być domyślnie zainstalowany. Jedyne, co może być potrzebne, to ustawienie odpowiedniej strefy czasowej za pomocą polecenia sudo dpkg-reconfigure tzdata. Aktualną godziną i strefę czasową możemy sprawdzić poleceniem date.
Możliwości programu cron są o wiele większe, niż te przedstawione w przykładzie. Możemy zlecać wykonywanie zadań w określonych godzinach, dniach i miesiącach podając ścieżki do skryptów lub programów. Zachęcam do eksperymentowania z tym narzędziem – w sieci znajdziemy mnóstwo materiałów dotyczących jego użycia.
KAmodRPi ADC_DAC – sterowniki modułu iio (Industrial Input Output)
Drugi z opisywanych modułów – KAmodRPi ADC_DAC, zawiera dwa przetworniki: MCP3021 i MCP4716. Co prawda w jądrze nie znajdziemy wsparcia dla tych urządzeń, ale możemy napisać dla nich sterowniki wzorując się na podobnych, znajdujących się w źródłach Linuksa. Do rozpoczęcia pracy potrzebujemy odpowiednich narzędzi do kompilowania oraz repozytorium ze źródłami.
Kompilator i kod jądra. Zestaw narzędzi potrzebnych do skompilowania jądra (tzw. toolchain) znajdziemy na stronie https://goo.gl/HVdBiQ. Po rozpakowaniu archiwum narzędzia są gotowe do użycia. Warto jednak dodać katalog gcc-linaro-arm-linux-gnueabihf-4.7-2013.04-20130415_linux/bin do zmiennej środowiskowej PATH (np. w pliku .bashrc w katalogu domowym), aby programy były dostępne z każdego miejsca w systemie, bez potrzeby podawania pełnej ścieżki.
Do pracy z jądrem Linuksa najwygodniej jest użyć systemu kontroli wersji git. Umożliwia on pobranie źródeł i ułatwia proces wprowadzania zmian. Wersję źródeł dla Odroida pobierzemy poleceniem git clone --depth 1 https://goo.gl/EtRpGa. Po jego wywołaniu na dysku lokalnym zostanie utworzona kopia repozytorium, na której możemy wykonywać i zapisywać dowolne zmiany. Katalog główny repozytorium możemy rozpoznać po mieszczącym się w nim ukrytym katalogu .git. Możemy go zobaczyć wywołując polecenie ls -al w konsoli. To w nim znajduje się cała historia zmian i wersji projektu. Aby ściągnąć najnowsze zmiany z serwera wystarczy wywołać w katalogu repozytorium git pull. Polecenie to powoduje ściągnięcie wszystkich zmian z serwera w stosunku do naszej lokalnej kopii. Operacja z pewnością się powiedzie o ile nie dokonaliśmy w międzyczasie żadnych zmian. W przeciwnym razie, w pierwszej kolejności musimy zapisać własne zmiany. Pomocne przy tym będą cztery polecenia:
git status
git add <nazwa_pliku>
git commit
git stash
Pierwsze polecenie pokazuje aktualny stan repozytorium. Po jego wywołaniu dowiemy się, które pliki zostały dodane lub zmienione. W pierwszym przypadku musimy wydać kolejne z poleceń służące dodawaniu plików do repozytorium. Należy o tym pamiętać, ponieważ pliki utworzone w katalogu głównym, lub podkatalogach repozytorium nie są dodawane automatycznie do kontroli wersji. Jeżeli sami o to nie zadbamy, możemy utracić zmiany, np. po przypadkowym usunięciu pliku.
Trzecie polecenie z powyższej listy wprowadza zmiany do repozytorium, w tym także pliki dodane poleceniem git add. Po wywołaniu jesteśmy proszeni o podanie komentarza. Warto zadbać o to, aby komentarze krótko, ale wyczerpująco opisywały wprowadzone zmiany, gdyż potem ułatwia to przeglądanie repozytorium i przywracanie poprzednich wersji plików.
Nie zawsze jednak chcemy, żeby nasze zmiany trafiły do repozytorium. Może się to zdarzyć, gdy ciągle pracujemy nad kodem źródłowym, ale nie jest on jeszcze gotowy. W tej sytuacji pomoże nam ostatnie z poleceń: git stash. Umożliwia ono bezpieczne zachowanie wszelkich dokonanych zmian, ale bez zapisywania ich w repozytorium. Dzięki temu możemy ściągnąć nową wersję projektu z serwera, a następnie przywrócić swoją pracę poleceniem git stash apply.
Sterownik MCP4716. Mając aktualne źródła Linuxa możemy rozpocząć pracę nad sterownikami. Przetworniki A/C i C/A, oraz wszelkiego rodzaju sensory, takie jak czujniki temperatury, akcelerometry itp. działają w ramach podsystemu iio (Industrial Input Output) [2]. Dzięki temu, urządzenia te, podłączone za pośrednictwem I2C lub SPI mają wspólny interfejs ułatwiający pisanie programów korzystających z ich sterowników.
Sterownik układu MCP4716 najłatwiej utworzyć korzystając z istniejącego sterownika MCP4725. Układy te różnią się rozdzielczością (MCP4716 ma 10 bitów, natomiast MCP4725 – 12 bitów) oraz obecnością pamięci EEPROM w drugim z nich. Zaczynamy więc od utworzenia nowego pliku w katalogu ze źródłami Linuxa: ./drivers/iio/dac/mcp4716.c i skopiowaniu do niego zawartości pliku ./drivers/iio/dac/mcp4725.c. Na potrzeby przykładu można pozostawić tylko podstawowe funkcje sterownika, a mianowicie zapis i odczyt wartości na wyjściu przetwornika. W ostatecznej wersji pliku powinny znaleźć się struktury i funkcje pokazane na listingu 1. Kod źródłowy sterownika można pobrać z repozytorium poleceniem git clone https://goo.gl/Nk5Ixr.
Aby sterownik mógł zostać skompilowany z całym jądrem należy jeszcze dokonać zmian w pliku ./drivers/iio/dac/Makefile, dopisując na końcu obj-$(CONFIG_MCP4716) += mcp4716.o oraz dodając opis przygotowanego sterownika w pliku ./drivers/iio/dac/Kconfig:
config MCP4716
tristate „MCP4716 DAC driver”
depends on I2C
---help---
Say Y here if you want to build a driver for the Microchip
MCP 4716 10-bit digital-to-analog converter (DAC) with I2C
interface.
To compile this driver as a module, choose M here: the module
will be called mcp4716.
Ostatnią zmianą jest dodanie przetwornika do drzewa urządzeń wczytywanego przez jądro i znajdującego się w pliku ./arch/arm/boot/dts/meson8b_odroidc.dts. W tym celu należy dopisać informację o nowym urządzeniu w sekcji magistrali I2C-A:
i2c@c1108500{ /*I2C-A*/
compatible = „amlogic,aml_i2c”;
dev_name = „i2c-A”;
status = „ok”;
reg = <0xc1108500 0x20>;
device_id = <1>;
pinctrl-names=”default”;
pinctrl-0=<&a_i2c_master>;
#address-cells = <1>;
#size-cells = <0>;
use_pio = <0>;
master_i2c_speed = <100000>;
mcp4716@60{
compatible = „microchip,mcp4716”;
reg = <0x60>;
};
};
Kompilowanie jądra
Z uwagi na to, że dodany sterownik ma znaleźć się bezpośrednio w jądrze, należy je teraz skonfigurować i skompilować. Pierwszym krokiem jest przygotowanie konfiguracji korzystając z domyślnych ustawień make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- odroidc_defconfig. Polecenie to możemy wywołać, jeżeli ścieżka do kompilatora znajduje się w zmiennych środowiskowych. Generuje ono plik .config w katalogu ze źródłami jądra, zawierający domyślną konfigurację jądra dla Odroida. Aby dostosować ją do własnych potrzeb można skorzystać z polecenia make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig umożliwiającego zmianę konfiguracji poprzez wygodny interfejs graficzny (rysunek 2). Komponenty mogą zostać dołączone do jądra (za pomocą klawisza „Y”), lub skompilowane jako moduły (za pomocą klawisza „M”). W tym przykładzie wszystko zostanie skompilowane razem z jądrem. Należy więc znaleźć i dodać następujące komponenty:
Device Drivers > Amlogic Device Drivers > I2C Hardware Bus support > Amlogic I2C Driver
Device Drivers > Industrial I/O support
Device Drivers > Industrial I/O support > Digital to analog converters > MCP4716 DAC driver
Po wyjściu z narzędzia menuconfig i zapisaniu zmian można przystąpić do kompilowania jądra wydając polecenie make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4. Parametr „-j” umożliwia równoległą kompilację na kilku rdzeniach procesora. Po zakończeniu kompilacji jądra należy jeszcze przygotować potrzebne moduły:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=../modules modules_install
Pierwsze polecenie kompiluje moduły, natomiast drugie kopiuje je do wskazanego katalogu. Pozostało już tylko przygotować obraz jądra (może być konieczne zainstalowanie pakietu u-boot-tools) i drzewo urządzeń dla bootloadera:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
Zbudowane komponenty należy skopiować na kartę pamięci. Na partycję BOOT muszą trafić obraz jądra (./arch/arm/boot/uImage) i drzewo urządzeń (./arch/arm/boot/dts/meson8b_odroidc.dtb), natomiast katalog z modułami należy skopiować na partycję z plikami systemowymi do katalogu lib.
Teraz można uruchomić system i sprawdzić działanie sterownika. W katalogu /sys/bus/iio/devices powinno znajdować się nowe urządzenie reprezentujące omawiany przetwornik, co można sprawdzić w pliku /sys/bus/iio/devices/iio:device0/name. Najważniejszym plikiem w podkatalogu urządzenia jest natomiast /sys/bus/iio/devices/iio:device0/out_voltage0_raw, do którego możemy wpisywać lub odczytywać 10-bitowe wartości wyjściowe przetwornika.
Krzysztof Chojnowski
Bibliografia:
https://goo.gl/DedF0O
https://goo.gl/bLNzvk
https://goo.gl/exmP06
https://goo.gl/XzUaUS
https://goo.gl/Rx6K4i
https://goo.gl/ugeYpH
https://goo.gl/mfHn27
https://goo.gl/sBQvM5