[Źródło: https://somlabs.com/product/visionsom-6ull/]
Obsługa portów GPIO możliwa jest dzięki sterownikom jądra systemu Linux. Do dyspozycji są piny zdefiniowane jako Led Class Driver jako wyjście, oraz piny wejściowe GPIO Buttons.
Zamiast sterownika dedykowanego dla wejścia lub wyjścia można wykorzystać ogólny sterownik pinów GPIO.
Diody led zostały podłączone pod wyprowadzenia GPIO10 (Dioda niebieska), GPIO11 (Dioda zielona), GPIO12 (Dioda żółta), GPIO13 (Dioda czerwona). Przyciski natomiast to piny GPIO3, GPIO4, GPIO8, GPIO9.
W celu uzyskania dostępu do diod należy wprowadzić zmiany w Device Tree. W tym celu najlepiej
posłużyć się systemem obsługiwanym z Linuxa.
Na samym początku należy pobrać obrazVisionSom-6ULL:
Kolejnym elementem jest zbudowanie jądra systemu oraz wykonanie komendy make. W tym celu należy przejść do odpowiedniego folderu:
Po czym wykonać operację kopiowania domyślnej konfiguracji do folderu z jądrem systemu:
Teraz można już uruchomić polecenie make z folderu do którego została skopiowana konfiguracja (/home/developer/source/kernel/linux-rel_imx_4.1.15_2.1.0_ga):
Po poprawnym wykonaniu powyższej czynności można przejść do modyfikacji Device Tree:
Poniżej opiszę tylko udostępnienie dla użytkownika pinów odpowiedzialnych za diody. Ponieważ domyślnie są one niedostępne. Inne sterowniki obsługą je do wyświetlania informacji np. o działającej transmisji danych.
Przyciski są automatycznie udostępnione wiec ich nie trzeba modyfikować.
W pliku należy znaleźć następujące informacje:
Aby udostępnić sterowanie pinami dla użytkownika to należy zakomentować odpowiednie części (usr0...usr3) znajdujące się wewnątrz nawiasów klamrowych dla definicji leds. Domyślnie są one przypisane do podsystemu, sterownika gpio-leds i sterowanie jest przeprowadzane przez domyślny trigger.
W celu sprawdzenia pozostałych definicji odpowiednich pinów oraz funkcji jakie są udostępnione poszczególnym pinom można sprawdzić w poniższym pliku:
Następnym elementem jest stworzenie pakietu Debiana:
Teraz należy przesłać plik na urządzenie:
W celu sprawdzeniu adresu IP urządzenia można skorzystać z jednej z dwóch komend:
Po przesłaniu pliku na płytkę należy zainstalować pakiet następującym poleceniem (tą komendę należy wywołać na płycie somlabs a nie na ubuntu jak wcześniejsze):
Gdy już operacja zostanie zakończona to w celu załadowania ustawień należy ponownie uruchomić system (np. polecenie reboot). Po ponownym uruchomieniu systemu można przejść do konfiguracji.
Poniżej chciałbym opisać sposobu konfiguracji i obsługi pinów przez polecenia skryptowe. Na samym początku obsługa diod czyli gpio jako wyjście.
Dostęp do danych udostępnianych przez sterownik GPIO należy szukać w folderze:
W opisanym folderze znajdują się dwa ważne pliki tzn. export oraz unexport. Dzięki nim można zająć lub zwolnić podane wyprowadzenie dla przestrzeni użytkownika. Oczywiście dane wyprowadzenie nie może być już wykorzystywane przez inny sterownik. Dlatego należało wykonać zmiany wyprowadzeń w Device Tree.
Aby wyeksportować dane wyprowadzenie należy wywołać polecenie:
Po tej operacji zostaje stworzony folder z opisem danego wyprowadzenia (dla gpio 12 będzie to folder gpio12 itp. itd.).
Teraz można przejść do utworzonego folderu wykorzystując następującą komendę:
Po tej operacji można wyświetlić informację z dostępnymi parametrami:
Najważniejsze z udostępnionych przez system plików to direction, value oraz edge. Poniżej krótki opis każdego z nich:
plik direction - odpowiada za kierunek działania danego wyprowadzenia (in/out)
plik value - odpowiada za ustawienie odpowiedniego stanu na pinie wyjściowym. W przypadku ustawienia jako wejścia ten plik będzie przechowywał aktualny stan na linii.
plik edge - pozwala na określenie jakie zbocze będzie wyzwalało zmianę stanu na linii. Dopuszczalne parametry do wpisania to none, rising, falling lub both.
Wobec tego w celu wybrania kierunku należy wprowadzić w konsoli następującą komendę:
Po ustawieniu kierunku należy wprowadzić odpowiedni stan:
Gdy ustawiony jest pin jako wyjściowy to stan linii można odczytać następującą komendą:
W celu wywołania poniższych skryptów wystarczy wpisać:
Prosty skrypt pozwalający na obsługę dostępnych diod może wyglądać następująco:
W celu obsługi większej liczby wyprowadzeń wystarczy rozbudować nieco skrypt dodając pozostałe wartości:
Przed użyciem powyższych dwóch skryptów należy uruchomić wyprowadzenia poleceniem export, oraz ustawić kierunek jako wyjściowy.
Tutaj należy pamiętać, że po ponownym uruchomieniu komputera zmiany zapisane w plikach /sys/class/gpio/ ulegną usunięciu. Wobec tego lepiej stosować skrypt ustawiający dane wyprowadzenia w odpowiednim stanie.
Nieco rozbudowana wersja uruchamiająca dane wyprowadzenie oraz ustawiająca kierunek działania:
Na samym początku skrypt sprawdza czy dane wyprowadzenie zostało już wyeksportowane, jeśli to to następuje wyeksportowanie danego wyprowadzenia. Po przejściu przez cztery piny dla diod ustawiany jest kierunek działania jako wyjście. Następnie piny ustawione zostają w stan niski i następuje wejście do pętli while wykonującej zapalanie oraz gaszenie diod.
Takie rozwiązanie pozwoli na uruchomienie skryptów zaraz po starcie systemu, dzięki temu nie będzie konieczności wprowadzania ręcznie informacji do sterownika gpio.
W celu obsługi przycisku należy wykonać następujące operacje:
Po takiej operacji ustawianą wartość przez przycisk można sprawdzić za pomocą polecenia cat. I tak przy nie wciśniętym przycisku wartość jest 1. Po jego kliknięciu wartość zmienia się na 0:
Teraz do skryptu dołożę obsługę przycisku tak aby po kliknięciu został przesłany tekst na wyświetlacz:
Poniżej przejdę do przedstawienia prostego programu pozwalającego na obsługę wyprowadzeń GPIO jako in/out.
Tutaj operacje wyglądają podobnie co przy plikach skryptowych. To znaczy dalej trzeba wyeksportować dane wyprowadzenie dla użytkownika, następnie trzeba ustawić kierunek i dopiero wtedy można przejść do zmiany wartości na pinie.
Poniżej przykład pozwalający na sterowanie diodami:
Funkcja odpowiedzialna za włączenie danego wyprowadzenia do przestrzeni użytkownika:
Operacje jak już wspomniałem wcześniej dotyczą operacji na plikach. Tym razem tylko nie są one wykonywane przez bash a przez instrukcję w języku C. Na samym początku zostaje otwarty plik export do którego zostaje wprowadzony numer pinu jaki ma być dostępny dla użytkownika.
Kolejna niezbędna funkcja pozwala na ustawienie kierunku działania danego wyprowadzenia:
Funkcja pozwalająca na ustawienie stanu wysokiego lub niskiego:
Poniżej sterowanie wszystkimi pinami z podłączonymi diodami (od IO10 do IO13):
Teraz przejdę do opisu obsługi przycisku. Tutaj podobnie jak w przypadku wersji dla bash należy odczytać dane z pliku value gdzie sprawdzany będzie aktualny stan pinu. W przykładzie wykorzystam przycisk podłączony pod pin 4.
Sprawdzanie przycisku będzie polegało na sprawdzaniu stanu zbocza w oparciu o funkcję asynchronicznego wejścia/wyjścia zdefiniowane w bibliotece poll.h.
Na samym początku należy ustawić zbocze jakim będzie wyzwalane przerwanie:
Tutaj następuje edycja pliku edge. Dla przypomnienia jako parametr można wprowadzić rising, falling lub both.
W celu uzyskania wartości z pliku value wykorzystam funkcję przekazującą deskryptor do otwartego pliku value.
Plik należy odczytać tylko do odczytu, ponieważ będzie z niego pobierana wartość dla pinu. Dodatkowo należy umieścić flagę O_NONBLOCK otwierająca plik w trybie nieblokującym.
Funkcja main do pętli while wygląda następująco:
Po deklaracji pinu oraz odczycie deskryptora dla pliku value należy przygotować dane dla struktury pollfd.
Do niej wprowadzany jest deskryptor do pliku value, rodzaj zdarzenia oraz jak długo nastąpi oczekiwanie na zdarzenie.
W pętli while następuje wywołanie funkcji poll po czym następuje sprawdzenie czy przycisk został wciśnięty po aktualizacji flagi w strukturze pollfd.
Po wciśnięciu przycisku nastąpi aktualizacja danych w pliku value.
Programy opisane w tym poście dotyczące części języka C można pobrać z dysku Google pod tym linkiem.
Zmiany w Device Tree [1]:
W celu uzyskania dostępu do diod należy wprowadzić zmiany w Device Tree. W tym celu najlepiej
posłużyć się systemem obsługiwanym z Linuxa.
Na samym początku należy pobrać obrazVisionSom-6ULL:
- sudo apt install qemu-user-static
- wget http://ftp.somlabs.com/somlabs-visionsom-6ull-debian-rootfs-qemu.tar.xz
- sudo tar -xvf somlabs-visionsom-6ull-debian-rootfs-qemu.tar.xz
- sudo cp /etc/hostname ~/somlabs-debian/etc/hostname
- sudo cp /etc/hosts ~/somlabs-debian/etc/hosts
- sudo ./somlabs-debian/chtoolchain
Kolejnym elementem jest zbudowanie jądra systemu oraz wykonanie komendy make. W tym celu należy przejść do odpowiedniego folderu:
- cd /home/developer/source/kernel/linux-rel_imx_4.1.15_2.1.0_ga
Po czym wykonać operację kopiowania domyślnej konfiguracji do folderu z jądrem systemu:
- cp /home/developer/source/somlabs-dts-1.0/visionsom-6ull-linux_defconfig /home/developer/source/kernel/linux-rel_imx_4.1.15_2.1.0_ga/.config
Teraz można już uruchomić polecenie make z folderu do którego została skopiowana konfiguracja (/home/developer/source/kernel/linux-rel_imx_4.1.15_2.1.0_ga):
- make deb-pkg -j8 CFLAGS_MODULE = -fno-pic
Po poprawnym wykonaniu powyższej czynności można przejść do modyfikacji Device Tree:
- nano /home/developer/source/somlabs-dts-1.0/somlabs-visionsom-6ull.dts
Poniżej opiszę tylko udostępnienie dla użytkownika pinów odpowiedzialnych za diody. Ponieważ domyślnie są one niedostępne. Inne sterowniki obsługą je do wyświetlania informacji np. o działającej transmisji danych.
Przyciski są automatycznie udostępnione wiec ich nie trzeba modyfikować.
W pliku należy znaleźć następujące informacje:
- leds{
- compatible = "gpio-leds";
- usr0 {
- label = "usr0";
- gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
- linux,default-trigger = "heartbeat";
- };
- usr1 {
- label = "usr1";
- gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
- linux,default-trigger = "mmc0";
- };
- usr2 {
- label = "usr2";
- gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
- linux,default-trigger = "mmc1";
- };
- usr3 {
- label = "usr3";
- gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>;
- };
- };
Aby udostępnić sterowanie pinami dla użytkownika to należy zakomentować odpowiednie części (usr0...usr3) znajdujące się wewnątrz nawiasów klamrowych dla definicji leds. Domyślnie są one przypisane do podsystemu, sterownika gpio-leds i sterowanie jest przeprowadzane przez domyślny trigger.
W celu sprawdzenia pozostałych definicji odpowiednich pinów oraz funkcji jakie są udostępnione poszczególnym pinom można sprawdzić w poniższym pliku:
- /home/developer/source/kernel/linux-rel_imx_4.1.15_2.1.0_ga/arch/arm/boot/dts/imx6ul-pinfunc.h
Następnym elementem jest stworzenie pakietu Debiana:
- cd /home/developer/source/somlabs-dts-1.0/
- /home/developer/source/somlabs-dts-1.0# dpkg-buildpackage -us -uc
Teraz należy przesłać plik na urządzenie:
- scp /home/developer/source/somlabs-dts_1.0.0_armhf.deb root@<IP_ADDR_SOMLABS>:/root/
W celu sprawdzeniu adresu IP urządzenia można skorzystać z jednej z dwóch komend:
- hostname -I
- //lub
- ip a
Po przesłaniu pliku na płytkę należy zainstalować pakiet następującym poleceniem (tą komendę należy wywołać na płycie somlabs a nie na ubuntu jak wcześniejsze):
- dpkg -i /root/somlabs-dts_1.0.0_armhf.deb
Gdy już operacja zostanie zakończona to w celu załadowania ustawień należy ponownie uruchomić system (np. polecenie reboot). Po ponownym uruchomieniu systemu można przejść do konfiguracji.
Sterowanie pinami polecenia konsoli:
Poniżej chciałbym opisać sposobu konfiguracji i obsługi pinów przez polecenia skryptowe. Na samym początku obsługa diod czyli gpio jako wyjście.
Dostęp do danych udostępnianych przez sterownik GPIO należy szukać w folderze:
- cd /sys/class/gpio/
W opisanym folderze znajdują się dwa ważne pliki tzn. export oraz unexport. Dzięki nim można zająć lub zwolnić podane wyprowadzenie dla przestrzeni użytkownika. Oczywiście dane wyprowadzenie nie może być już wykorzystywane przez inny sterownik. Dlatego należało wykonać zmiany wyprowadzeń w Device Tree.
Aby wyeksportować dane wyprowadzenie należy wywołać polecenie:
- echo 12 > /sys/class/gpio/export
- //lub jesli wskaźnik jest juz w folderze gpio to
- echo 12 > export
Po tej operacji zostaje stworzony folder z opisem danego wyprowadzenia (dla gpio 12 będzie to folder gpio12 itp. itd.).
Teraz można przejść do utworzonego folderu wykorzystując następującą komendę:
- cd /sys/class/gpio/gpio12
Po tej operacji można wyświetlić informację z dostępnymi parametrami:
Najważniejsze z udostępnionych przez system plików to direction, value oraz edge. Poniżej krótki opis każdego z nich:
plik direction - odpowiada za kierunek działania danego wyprowadzenia (in/out)
plik value - odpowiada za ustawienie odpowiedniego stanu na pinie wyjściowym. W przypadku ustawienia jako wejścia ten plik będzie przechowywał aktualny stan na linii.
plik edge - pozwala na określenie jakie zbocze będzie wyzwalało zmianę stanu na linii. Dopuszczalne parametry do wpisania to none, rising, falling lub both.
Wobec tego w celu wybrania kierunku należy wprowadzić w konsoli następującą komendę:
- //Jako wejście (np. przyciski)
- echo in > /sys/class/gpio/gpio12/direction
- //Jako wyjście (np. diody)
- echo out > /sys/class/gpio/gpio12/direction
Po ustawieniu kierunku należy wprowadzić odpowiedni stan:
- //Ustawienie stanu wysokiego
- echo 1 > /sys/class/gpio/gpio12/direction
- //Ustawienie stanu niskiego
- echo 0 > /sys/class/gpio/gpio12/direction
Gdy ustawiony jest pin jako wyjściowy to stan linii można odczytać następującą komendą:
- cat /sys/class/gpio/gpio12/value
W celu wywołania poniższych skryptów wystarczy wpisać:
- /root/blink.sh
Prosty skrypt pozwalający na obsługę dostępnych diod może wyglądać następująco:
- #!/bin/sh
- SLEEP="/bin/sleep"
- GpioData=/sys/class/gpio/gpio12
- while true ; do
- echo 1 > $GpioData/value
- $SLEEP 1
- echo 0 > $GpioData/value
- $SLEEP 1
- done
W celu obsługi większej liczby wyprowadzeń wystarczy rozbudować nieco skrypt dodając pozostałe wartości:
- #!/bin/sh
- LED1=10
- LED2=11
- LED3=12
- LED4=13
- SLEEP="/bin/sleep"
- GPIOVALUE_1=/sys/class/gpio/gpio$LED1
- GPIOVALUE_2=/sys/class/gpio/gpio$LED2
- GPIOVALUE_3=/sys/class/gpio/gpio$LED3
- GPIOVALUE_4=/sys/class/gpio/gpio$LED4
- echo 0 > $GPIOVALUE_1/value
- echo 0 > $GPIOVALUE_2/value
- echo 0 > $GPIOVALUE_3/value
- echo 0 > $GPIOVALUE_4/value
- while true ; do
- echo 1 > $GPIOVALUE_1/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_2/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_3/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_4/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_4/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_3/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_2/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_1/value
- $SLEEP 1
- done
Przed użyciem powyższych dwóch skryptów należy uruchomić wyprowadzenia poleceniem export, oraz ustawić kierunek jako wyjściowy.
Tutaj należy pamiętać, że po ponownym uruchomieniu komputera zmiany zapisane w plikach /sys/class/gpio/ ulegną usunięciu. Wobec tego lepiej stosować skrypt ustawiający dane wyprowadzenia w odpowiednim stanie.
Nieco rozbudowana wersja uruchamiająca dane wyprowadzenie oraz ustawiająca kierunek działania:
- #!/bin/sh
- SLEEP="/bin/sleep"
- GPIO_EXPORT=/sys/class/gpio/export
- LED1=10
- LED2=11
- LED3=12
- LED4=13
- GPIOVALUE_1=/sys/class/gpio/gpio$LED1
- GPIOVALUE_2=/sys/class/gpio/gpio$LED2
- GPIOVALUE_3=/sys/class/gpio/gpio$LED3
- GPIOVALUE_4=/sys/class/gpio/gpio$LED4
- if [ ! -d "$GPIOVALUE_1" ]; then
- echo "Export GPIO$LED1"
- echo $LED1 > $GPIO_EXPORT
- else
- echo "GPIO$LED1 exported"
- fi
- if [ ! -d "$GPIOVALUE_2" ]; then
- echo "Export GPIO$LED2"
- echo $LED2 > $GPIO_EXPORT
- else
- echo "GPIO$LED2 exported"
- fi
- if [ ! -d "$GPIOVALUE_3" ]; then
- echo "Export GPIO$LED3"
- echo $LED3 > $GPIO_EXPORT
- else
- echo "GPIO$LED3 exported"
- fi
- if [ ! -d "$GPIOVALUE_4" ]; then
- echo "Export GPIO$LED4"
- echo $LED4 > $GPIO_EXPORT
- else
- echo "GPIO$LED4 exported"
- fi
- echo "Set pin as output"
- echo out > $GPIOVALUE_1/direction
- echo out > $GPIOVALUE_2/direction
- echo out > $GPIOVALUE_3/direction
- echo out > $GPIOVALUE_4/direction
- echo "Set pin state to low"
- echo 0 > $GPIOVALUE_1/value
- echo 0 > $GPIOVALUE_2/value
- echo 0 > $GPIOVALUE_3/value
- echo 0 > $GPIOVALUE_4/value
- echo "Enter into while loop"
- while true ; do
- echo 1 > $GPIOVALUE_1/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_2/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_3/value
- $SLEEP 1
- echo 1 > $GPIOVALUE_4/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_4/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_3/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_2/value
- $SLEEP 1
- echo 0 > $GPIOVALUE_1/value
- $SLEEP 1
- done
Na samym początku skrypt sprawdza czy dane wyprowadzenie zostało już wyeksportowane, jeśli to to następuje wyeksportowanie danego wyprowadzenia. Po przejściu przez cztery piny dla diod ustawiany jest kierunek działania jako wyjście. Następnie piny ustawione zostają w stan niski i następuje wejście do pętli while wykonującej zapalanie oraz gaszenie diod.
Takie rozwiązanie pozwoli na uruchomienie skryptów zaraz po starcie systemu, dzięki temu nie będzie konieczności wprowadzania ręcznie informacji do sterownika gpio.
W celu obsługi przycisku należy wykonać następujące operacje:
- echo 4 > /sys/class/gpio/export
- echo in > /sys/class/gpio/gpio4/direction
Po takiej operacji ustawianą wartość przez przycisk można sprawdzić za pomocą polecenia cat. I tak przy nie wciśniętym przycisku wartość jest 1. Po jego kliknięciu wartość zmienia się na 0:
Teraz do skryptu dołożę obsługę przycisku tak aby po kliknięciu został przesłany tekst na wyświetlacz:
- #!/bin/sh
- SLEEP="/bin/sleep"
- GPIO_EXPORT=/sys/class/gpio/export
- BUTTONPIN=4
- INPUTVALUE=/sys/class/gpio/gpio$BUTTONPIN
- if [ ! -d "$INPUTVALUE" ]; then
- echo "Export Button on GPIO$BUTTONPIN"
- echo $BUTTONPIN > $GPIO_EXPORT
- else
- echo "Button on GPIO$BUTTONPIN exported"
- fi
- echo "Button config"
- echo in > $INPUTVALUE/direction
- echo "Enter into while loop"
- while true ; do
- var="$(cat $INPUTVALUE/value)"
- if [ $var -eq 0 ]; then
- echo "Button Clicked"
- fi
- done
Sterowanie pinami w c:
Poniżej przejdę do przedstawienia prostego programu pozwalającego na obsługę wyprowadzeń GPIO jako in/out.
Tutaj operacje wyglądają podobnie co przy plikach skryptowych. To znaczy dalej trzeba wyeksportować dane wyprowadzenie dla użytkownika, następnie trzeba ustawić kierunek i dopiero wtedy można przejść do zmiany wartości na pinie.
Poniżej przykład pozwalający na sterowanie diodami:
Funkcja odpowiedzialna za włączenie danego wyprowadzenia do przestrzeni użytkownika:
- static int gpioExportPin(unsigned int gpioPinNumber)
- {
- int fileDesc = -1;
- int writeBufferSize = -1;
- char buf[50] = {0x00};
- //Open file
- fileDesc = open(GPIO_MAIN_DIR "/export", O_WRONLY);
- //Check if operation error
- if(fileDesc < 0)
- {
- perror("File gpio - export Error");
- return fileDesc; //-1
- }
- //Prepare buffer
- writeBufferSize = snprintf(buf, sizeof(buf), "%d", gpioPinNumber);
- write(fileDesc, buf, writeBufferSize);
- close(fileDesc);
- return 0;
- }
Operacje jak już wspomniałem wcześniej dotyczą operacji na plikach. Tym razem tylko nie są one wykonywane przez bash a przez instrukcję w języku C. Na samym początku zostaje otwarty plik export do którego zostaje wprowadzony numer pinu jaki ma być dostępny dla użytkownika.
Kolejna niezbędna funkcja pozwala na ustawienie kierunku działania danego wyprowadzenia:
- static int gpioSelectDirection(unsigned int gpioPinNumber, unsigned int gpioDi$
- {
- int fileDesc = -1;
- char buf[100] = {0x00};
- snprintf(buf, sizeof(buf), GPIO_PIN_DIR "%d/direction", gpioPinNumber);
- fileDesc = open(buf, O_WRONLY);
- //Check FileDesc
- if (fileDesc < 0)
- {
- perror("Write gpio - direction Error");
- return fileDesc; //-1
- }
- //Write Direction to file
- if(gpioDirection == GPIO_OUT)
- {
- write(fileDesc, "out", sizeof("out"));
- }
- else
- {
- write(fileDesc, "in", sizeof("in"));
- }
- close(fileDesc);
- return 0;
- }
Funkcja pozwalająca na ustawienie stanu wysokiego lub niskiego:
- static int gpioSetValue(unsigned int gpioPinNumber, unsigned int gpioPinValue)
- {
- int fileDesc = -1;
- char buf[100] = {0x00};
- snprintf(buf, sizeof(buf), GPIO_PIN_DIR "%d/value", gpioPinNumber);
- fileDesc = open(buf, O_WRONLY);
- if(fileDesc < 0)
- {
- perror ("FileDescriptor gpio set value ERROR");
- return fileDesc;
- }
- if (gpioPinValue == 1) //high
- {
- write(fileDesc, "1", sizeof("1"));
- }
- else //low
- {
- write(fileDesc, "0", sizeof("0"));
- }
- close(fileDesc);
- return 0;
- }
Poniżej sterowanie wszystkimi pinami z podłączonymi diodami (od IO10 do IO13):
- int main (void)
- {
- if((gpioExportPin(GPIO_PIN_10) < 0) ||
- (gpioExportPin(GPIO_PIN_11) < 0) ||
- (gpioExportPin(GPIO_PIN_12) < 0) ||
- (gpioExportPin(GPIO_PIN_13) < 0))
- {
- exit (EXIT_FAILURE);
- }
- if((gpioSelectDirection(GPIO_PIN_10, GPIO_OUT) < 0) ||
- (gpioSelectDirection(GPIO_PIN_11, GPIO_OUT) < 0) ||
- (gpioSelectDirection(GPIO_PIN_12, GPIO_OUT) < 0) ||
- (gpioSelectDirection(GPIO_PIN_13, GPIO_OUT) < 0))
- {
- exit (EXIT_FAILURE);
- }
- while (1)
- {
- gpioSetValue(GPIO_PIN_10, 1);
- gpioSetValue(GPIO_PIN_11, 1);
- gpioSetValue(GPIO_PIN_12, 1);
- gpioSetValue(GPIO_PIN_13, 1);
- sleep (5);
- gpioSetValue(GPIO_PIN_10, 0);
- gpioSetValue(GPIO_PIN_11, 0);
- gpioSetValue(GPIO_PIN_12, 0);
- gpioSetValue(GPIO_PIN_13, 0);
- sleep (5);
- }
- return EXIT_SUCCESS;
- }
Teraz przejdę do opisu obsługi przycisku. Tutaj podobnie jak w przypadku wersji dla bash należy odczytać dane z pliku value gdzie sprawdzany będzie aktualny stan pinu. W przykładzie wykorzystam przycisk podłączony pod pin 4.
Sprawdzanie przycisku będzie polegało na sprawdzaniu stanu zbocza w oparciu o funkcję asynchronicznego wejścia/wyjścia zdefiniowane w bibliotece poll.h.
Na samym początku należy ustawić zbocze jakim będzie wyzwalane przerwanie:
- static int gpioSelectEdge(unsigned int gpioPinNumber, char *gpioEdge)
- {
- int fileDesc;
- char buf[70];
- snprintf(buf, sizeof(buf), GPIO_PIN_DIR "%d/edge", gpioPinNumber);
- fileDesc = open(buf, O_WRONLY);
- if(fileDesc < 0)
- {
- perror ("Set gpio - edge ERROR");
- return fileDesc;
- }
- if(gpioEdge == 1) //RISING
- {
- write(fileDesc, "rising", sizeof("rising"));
- }
- else if(gpioEdge == 2) //FALLING
- {
- write(fileDesc, "falling", sizeof("falling"));
- }
- else //BOTH
- {
- write(fileDesc, "both", sizeof("both"));
- }
- close(fileDesc);
- return 0;
- }
Tutaj następuje edycja pliku edge. Dla przypomnienia jako parametr można wprowadzić rising, falling lub both.
W celu uzyskania wartości z pliku value wykorzystam funkcję przekazującą deskryptor do otwartego pliku value.
- static int gpioValueFileDesc(unsigned int gpioPinNumber)
- {
- int fileDesc;
- char buf[60];
- snprintf(buf, sizeof(buf), GPIO_MAIN_DIR "%d/value", gpioPinNumber);
- fileDesc = open(buf, O_RDONLY | O_NONBLOCK);
- if(fileDesc < 0)
- {
- perror("Open value file Error");
- }
- return fileDesc;
- }
Plik należy odczytać tylko do odczytu, ponieważ będzie z niego pobierana wartość dla pinu. Dodatkowo należy umieścić flagę O_NONBLOCK otwierająca plik w trybie nieblokującym.
Funkcja main do pętli while wygląda następująco:
- int main(void)
- {
- struct pollfd pollFdStruct[1];
- int structNumbers = 1;
- int fileDesc = -1;
- int pollOperStat = 0;
- char buf[2] = {0x00};
- if (gpioExportPin(GPIO_PIN_BTN_4) < 0) {
- exit(EXIT_FAILURE);
- }
- if (gpioSelectDirection(GPIO_PIN_BTN_4, GPIO_IN) < 0) {
- exit(EXIT_FAILURE);
- }
- if (gpioSelectEdge(GPIO_PIN_BTN_4, 1) < 0) {
- exit(EXIT_FAILURE);
- }
- fileDesc = gpioValueFileDesc(GPIO_PIN_BTN_4);
- if (fileDesc < 0){
- exit(EXIT_FAILURE);
- }
- //Set offset
- lseek(fileDesc, 0, SEEK_SET);
- //ReadFile
- read(fileDesc, &buf, 2);
- printf(&buf);
- //Struct Descriptor
- //Write file descriptor to file
- pollFdStruct[0].fd = fileDesc;
- //Set event flag
- //High priority data may be read without blocking
- pollFdStruct[0].events = POLLPRI;
- pollFdStruct[0].revents = -1;
- //...
- //...
- }
Po deklaracji pinu oraz odczycie deskryptora dla pliku value należy przygotować dane dla struktury pollfd.
Do niej wprowadzany jest deskryptor do pliku value, rodzaj zdarzenia oraz jak długo nastąpi oczekiwanie na zdarzenie.
W pętli while następuje wywołanie funkcji poll po czym następuje sprawdzenie czy przycisk został wciśnięty po aktualizacji flagi w strukturze pollfd.
- while (1)
- {
- //Enable Poll, if success number > 0
- //if 0 then timeout
- pollOperStat = poll(pollFdStruct, structNumbers, 2000);
- //Check if Error occure
- if(pollOperStat < 0)
- {
- printf("poll function ERROR\n");
- break;
- }
- else if(pollOperStat == 0)
- {
- printf("poll function timeout\n");
- }
- if (pollFdStruct[0].revents & POLLPRI)
- {
- printf ("poll(): GPIO_%d interrupt occurred\n", GPIO_PI$
- lseek(pollFdStruct[0].fd, 0, SEEK_SET);
- read(pollFdStruct[0].fd, &buf, 2);
- }
- }//close while
- close(fileDesc);
- return EXIT_FAILURE;
- } //close main
Po wciśnięciu przycisku nastąpi aktualizacja danych w pliku value.
Programy opisane w tym poście dotyczące części języka C można pobrać z dysku Google pod tym linkiem.