※ 이 글에 사용된 마이크로프로세서는 TI사의 Sitara AM1808을 사용하였습니다.

 

리눅스 개발을 처음 접했을 때 주변 기기를 제어할 줄 몰라 난감했던 기억이 있습니다.

 

그나마 SPI나 I2C 등을 이용하는 것은 /dev 디렉터리에 있는 노드들을 사용해서 제어하는 방법은 여기 저기 잘 정리되어 별문제 없었지만, GPIO는 따로 노드가 존재하지 않아서 간단한 조작조차 리눅스 디바이스 드라이버로 만들어서 제어했던 기억이 있습니다...

 

그래서 간단히(?) 리눅스 어플리케이션에서 GPIO를 사용하는 방법을 정리해 볼까 합니다.

 

먼저, 리눅스 커널 빌드 옵션을 설정합니다.

 Device Drivers --->
 -*- GPIO Support
     [*] /sys/class/gpio/... (sysfs interface)

 

위와 같이 옵션을 설정하고 컴파일한 리눅스 커널로 부팅해 보면 다음과 같이 '/sys/class/gpio' 디렉터리가 생성된 것을 보실 수 있습니다.

 root@am180x-evm:~# ls /sys/class/gpio
 export       gpiochip128  gpiochip64   unexport
 gpiochip0    gpiochip32   gpiochip96

 

그럼, 예제로 GPIO 10번핀(GP0[10])을 제어해보도록 하겠습니다.

 root@am180x-evm:~# echo "10" > /sys/class/gpio/export

 

export를 하고나면 다음과 같이 'gpio10' 디렉터리가 생성된 것을 확인하실 수 있습니다.

 root@am180x-evm:~# ls /sys/class/gpio
 export       gpiochip0    gpiochip32   gpiochip96
 gpio10       gpiochip128  gpiochip64   unexport

 

그리고, 'gpio10' 디렉터리에 다음과 같은 파일이 생성된 것도 확인하실 수 있습니다.

 root@am180x-evm:~# ls /sys/class/gpio10
 active_low  edge        subsystem   value
 direction   power       uevent
  1. direction과 value도 export를 할 때와 같은 방법으로 변경할 수 있습니다.

 

GPIO 핀을 출력으로 설정할 경우 (핀 상태가 Low로 설정됩니다.)

 root@am180x-evm:~# echo "out" > /sys/class/gpio/gpio10/direction
  1. "out" 대신 "low"를 사용하셔도 됩니다.

 

GPIO 핀을 출력으로 설정하고, 핀 상태를 High로 설정할 경우

 root@am180x-evm:~# echo "high" > /sys/class/gpio/gpio10/direction

 

GPIO 핀을 입력으로 설정할 경우

 root@am180x-evm:~# echo "in" > /sys/class/gpio/gpio10/direction

 

GPIO 핀이 출력일 경우 핀 상태를 Low로 설정할 경우

 root@am180x-evm:~# echo "0" > /sys/class/gpio/gpio10/value

 

GPIO 핀이 출력일 경우 핀 상태를 High로 설정할 경우

 root@am180x-evm:~# echo "1" > /sys/class/gpio/gpio10/value

 

그럼 이제 위에 컨트롤한 것을 프로그래밍 언어로 코딩하면 어플리케이션에서 제어할 수 있겠죠~

 

아래 예제 코드는 GPIO 인터럽트를 폴링으로 읽어오는 방법도 소개하고 있습니다.

 

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 
 #define GPIO_OUTPUT 0
 #define GPIO_INPUT 1
 #define GPIO_HIGH  1
 #define GPIO_LOW  0
 
 #define GPIO_NONE  "none"
 #define GPIO_FALLING "falling"
 #define GPIO_RISING "rising"
 #define GPIO_BOTH  "both"
 
 #define SYSFS_GPIO_DIR "/sys/class/gpio"
 
 #define MAX_BUF 64
 
 int gpio_export(unsigned int gpio)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't export GPIO %d pin: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     len = snprintf(buf, sizeof(buf), "%d", gpio);
     write(fd, buf, len);
     close(fd);
 
     return 0;
 }
 
 int gpio_unexport(unsigned int gpio)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't unexport GPIO %d pin: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     len = snprintf(buf, sizeof(buf), "%d", gpio);
     write(fd, buf, len);
     close(fd);
 
     return 0;
 }
 
 int gpio_get_dir(unsigned int gpio, unsigned int *dir)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
 
     fd = open(buf, O_RDONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't get GPIO %d pin direction: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     read(fd, &buf, MAX_BUF);
     close(fd);
 
     if (stricmp(buf, "in") == 0)
         *dir = GPIO_INPUT;
     else
         *dir = GPIO_OUTPUT;
 
     return 0;
 }
 
 int gpio_set_dir(unsigned int gpio, unsigned int dir, unsigned int val)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);
 
     fd = open(buf, O_WRONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't set GPIO %d pin direction: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     if (dir == GPIO_OUTPUT) {
         if (val == GPIO_HIGH)
             write(fd, "high", 5);
         else
             write(fd, "out", 4);
     } else {
         write(fd, "in", 3);
     }
 
     close(fd);
 
     return 0;
 }
 
 int gpio_get_val(unsigned int gpio, unsigned int *val)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
 
     fd = open(buf, O_RDONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't get GPIO %d pin value: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     read(fd, buf, 1);
     close(fd);
 
     if (*buf != '0')
         *val = GPIO_HIGH;
     else
         *val = GPIO_LOW;
 
     return 0;
 }
 
 int gpio_set_val(unsigned int gpio, unsigned int val)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
 
     fd = open(buf, O_WRONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't set GPIO %d pin value: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     if (val == GPIO_HIGH)
         write(fd, "1", 2);
     else
         write(fd, "0", 2);
 
     close(fd);
 
     return 0;
 }
 
 int gpio_set_edge(unsigned int gpio, char *edge)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
 
     fd = open(buf, O_WRONLY);
 
     if (fd < 0) {
         fprintf(stderr, "Can't set GPIO %d pin edge: %s\n", gpio, strerror(errno));
         return fd;
     }
 
     write(fd, edge, strlen(edge)+1);
     close(fd);
 
     return 0;
 }
 
 int gpio_open(unsigned int gpio)
 {
     int fd, len;
     char buf[MAX_BUF];
 
     len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
 
     fd = open(buf, O_RDONLY|O_NONBLOCK);
 
     if (fd < 0)
         fprintf(stderr, "Can't open GPIO %d pin: %s\n", gpio, strerror(errno));
 
     return fd;
 }
 
 int gpio_close(int fd)
 {
     return close(fd);
 }
 
 int gpio_read(int fd, unsigned int *val)
 {
     int ret;
     char ch; 
 
     lseek(fd, 0, SEEK_SET);
 
     ret = read(fd, &ch, 1);
 
     if (ret != 1) {
         fprintf(stderr, "Can't read GPIO %d pin: %s\n", gpio, strerror(errno));
         return ret;
     }
 
     if (ch != '0')
         *val = GPIO_HIGH;
     else
         *val = GPIO_LOW;
 
     return 0;
 }
 
 int main(int argc, char *argv[])
 {
     unsigned int gpio;
     unsigned int val;
     char *end_ptr;
 
     if (argc < 3)
         exit(1);
 
     if (strcmp(argv[1], "r") == 0) {
         gpio = strtoul(argv[2], &end_ptr, 0);
         gpio_export(gpio);
         gpio_set_input(gpio);
         gpio_get_val(gpio, &val);
         gpiof_unexport(gpio);
         fprintf(stderr, "gpio%d = %d\n", gpio, val);
     } else if (strcmp(argv[1], "w") == 0) {
         if (argc < 4)
             exit(1);
 
         gpio = strtoul(argv[2], &end_ptr, 0);
         val = strtoul(argv[3], &end_ptr, 0);
         gpio_export(gpio);
         gpio_set_output(gpio, val);
         gpio_unexport(gpio);
     } else if (strcmp(argv[1], "i") == 0) {
         int fd;
         struct pollfd fdset[2];
         int ret;
 
         if (argc < 4)
             exit(1);
 
         gpio = strtoul(argv[2], &end_ptr, 0);
         gpio_export(gpio);
         gpio_set_input(gpio);
         gpiof_set_edge(gpio, argv[3]); // "none", "falling", "rising", "both"
         fd = gpio_open(gpio);
         gpio_read(fd, &val);
 
         while (1) {
             memset(fdset, 0, sizeof(fdset));
             fdset[0].fd = STDIN_FILENO;
             fdset[0].events = POLLIN;
             fdset[1].fd = fd;
             fdset[1].events = POLLPRI;
             ret = poll(fdset, 2, 3*1000);
 
             if (ret < 0) {
                 perror("poll");
                 break;
             }
 
             fprintf(stderr, ".");
 
             if (fdset[1].revents & POLLPRI) {
                 fprintf(stderr, "\nGPIO %d interrupt occurred!\n", gpio);
                 gpio_read(fdset[1].fd, &val);
             }
 
             if (fdset[0].revents & POLLIN)
                 break;
 
             fflush(stdout);
         }
 
         gpio_close(fd);
         gpio_unexport(gpio);
     } else {
         exit(1);
     }
 
     return 0;
 }

  1. AM1808의 GPIO를 사용하시기에 앞서 리눅스 커널에서 PinMux 설정을 하셔야 합니다.

 

참고 사이트

Posted by kyudoc

댓글을 달아 주세요