[ BBB - yocto (8) ] gpio

0) 참고하면 좋은 자료

https://github.com/derekmolloy/boneDeviceTree/tree/master/docs


어떤 핀 헤더에 따른 핀 정보가 다 나와있다. 핀 번호를 참조해서 하면 된다.


핀헤더에 네이밍에 대한 정보는 bbb pinout을 검색해보면 된다.



1) gpio 제어 방법 소개

1. sysfs를 사용하는 방법

커널이 /sys/class/gpio쪽에 gpio폴더를 만들어둔다. 여기서 제어하는 방법은 아래 bash명령어를 따라해 주면 된다.

#output 설정의 경우
$ echo 'PIN번호' > /sys/class/gpio/export

#gpio output 설정
$ echo out > /sys/class/gpio/gpio'PIN번호'/direction

#gpio high
$ echo 1 > /sys/class/gpio/gpio'PIN번호'/value

#gpio low
$ echo 0 > /sys/class/gpio/gpio'PIN번호'/value

#gpio input 설정
$ echo in > /sys/class/gpio/gpio'PIN번호'/direction

#gpio 읽는 법
$ cat /sys/class/gpio/gpio'PIN번호'/value

c에서 명령어를 실행하는 방법은 bash 명령어를 실행할 수 있는 <stdlib.h>의 system 함수를 쓰면 된다.


파일을 읽으려면 open함수로 파일을 열고 read로 읽어주면 된다.


그런데 sysfs 제어 방식은 추천하지 않는다. 

GPIO Sysfs Interface for Userspace — The Linux Kernel documentation


이 api는 개발 중단되었으며, 향후 없어질 것이라는 내용이다. 


앞으로 gpio를 짜는 어플을 개발한다면, /dev/gpiochip*을 사용하여 gpio 드라이버를 사용하는 방식을 쓰는게 좋을것이다.


2. gpio 드라이버를 사용하는 방법

/dev/gpioinfo*를 열어서 사용한다.


open으로 /dev/gpioinfo*를 열며, ioctl함수로 gpio 드라이버에 구조체를 넘겨줘 gpio를 제어하는 방식이다.


int ioctl(int fd, unsigned long request, ...);

ioctl 함수는 디바이스 드라이버에 특정 값을 넘겨주거나, 드라이버로부터 특정 값을 넘겨 받을 수 있다.


fd에는 파일디스크립터이다.


나머지 함수 인자는 드라이버에 다라 바뀐다.


성공시 0을 반환하며, 실패시 -1을 반환한다. 


사용하려면 'sys/ioctl.h'를 인클루드하면 된다.


3. /dev/gpiochip*이 없거나 /sys/class/gpio 파일이 없을 때

기본적으로 커널 설정에서 gpio의 sysfs와 /dev/gpiochip*을 사용할 수 있게 되어 있지만


만약 파일이 보이지 않는 경우 커널 설정을 해주어야 한다.


poky 폴더로 이동하여 다음 명령어를 실행한다.

$ source oe-init-build-env
$ bitbake -c menuconfig virtual/kernel

그러면 작은 창 하나가 나올텐데 (창이 안나오면 sudo apt-get install screen 실행)


general setup에 들어간 다음


configure standard kernel features를 y를 눌러서 켜준다. 참고로 맨위에 커널 메뉴 콘피그 사용법에 대하여 나와있다.

y를 누르면 커널 설정을 포함하여 빌드하며, n을 누르면 제외, M은 커널 모듈로 만들어 빌드한다.

esc키를 2번 누르거나 키보드 커널 메뉴 콘피그 맨 아래 부분의 화살표 왼쪽 오른쪽 키로로 exit 선택후 엔터 쳐서 나온다.

그리고 디바이스 드라이버 메뉴를 찾는다.


디바이스 드라이버를 선택하고 엔터를 친다.


gpio support를 선택하고 엔터를 친다.


/sys/class/gpio/...와 character device 두 개의 메뉴를 y를 쳐서 활성화한다.


save를 눌러 저장한다. esc키를 계속 누르거나 exit키를 계속 눌러서 나가준다.


다시 bitbake core-image-full-cmdline 명령어로 빌드해주면 된다.


빌드한 이후 파티션 1번의 커널 이미지 zImage를 교체해 주어야 한다.









2) gpio 드라이버 제어하기

1. gpio 선택하기


gpiochip을 사용할 때는 gpiochip과 line이라는 것을 사용한다. line은 gpio 핀이라 생각하면 되겠다.


gpiochip마다 line을 가지는데 gpioinfo를 찍어보면 알 수 있다.


yocto에서 빌드한 이미지 파일에 gpioinfo를 사용하려면, poky/build/conf/local.conf에 다음 줄을 추가한다.


IMAGE_INSTALL:append=" libgpiod libgpiod-tools libgpiod-dev"

그리고 bitbake를 사용해 libgpiod를 설치한 다음 파일시스템을 다시 빌드한다.

$ bitbake core-image-full-cmdline

비글본 sd카드의 파일시스템을 교체해주면 된다.


sd카드에 알아서 플래싱하는 쉘 스크립트를 작성했다. 아래 글을 보고 따라하면 커맨드 하나로 편하게 할 수 있다.

https://leejunggyun.blogspot.com/2025/02/sd.html


아무튼 gpioinfo를 yocto 이미지에 포함한 다음 실행해보자

이처럼 gpio핀이 어떤 핀인지, 사용자는 누구인지, 핀 속성(출력, 입력)이 표시된다.


따라서 이를 보고 어떤 핀(라인)을 사용할 것인지 결정하면 된다.


/usr/include/linux/gpio.h로 가면 구조체에 대한 주석이 설명으로 자세히 나와 있다.


gpio v2는 동시에 gpio를 사용할 수 있다.


 1. struct gpio_v2_line_request - gpio 라인 정보 요청

 * offsets: gpio 라인이 들어가는 배열, 아까 gpioinfo로 찍은 라인을 넣으면 된다. 최대 64개

 * consumer: 사용자라 생각하면 된다. 위 gpioinfo로 찍으면 "PHY reset" 부분이다.

 * config: 구조체로 gpio에 대한 설정을 할 수 있다. (gpio 초기 값, 속성 , 이벤트 등)

 * num_lines: 요청할 라인의 숫자, offsets에 사용할 gpio 핀 수만큼 적어주면 된다. 최대 64개

 * event_buffer_size: 이벤트 버퍼의 크기, 0을 넣으면 16 * num_lines 이다.

 * padding: 안쓰는 필드

 * fd: offsets에 설정한 gpio 라인들에 대한 파일 핸들러를 반환한다. 0이나 음수는 에러를 의미한다.


1.2. struct  gpio_v2_line_config - gpio 라인들에 대한 설정

 * flags: gpio_v2_line_flag 열거형에 적힌 플래그, gpio output이나 input 혹은 pull up, down등을 설정한다. 각 라인이 @attrs에서 쓰는 설정에 따라 덮어씌워진다.

 * @num_attrs: @attrs 의 수

 * @padding: 안 쓰는 필드

 * @attrs: 요청된 라인과 관련있는 설정, 최대 10개 설정 가능하다. 여기서 gpio 초기 값이라던가, 라인이 여러개인 경우 flag를 각각 설정한다던가, "debounce"라는 스위치 같은거 사용할 때 바운스 현상을 보정하는 us를 설정한다던가 할 수 있다.


1.3 ioctl로 gpio드라이버에 값 요청

gpiochip0번의 13번 라인을 열어서 output으로 설정한다고 해보자 그러면



2. struct gpio_v2_line_values - gpio 라인의 값을 가져온다.

 * @bits: 여기서 bits는 offsets에서 gpio_v2_line_request 설정한 line에 대한 비트 값이다. 예를 들어 offsets[0]에 P8_11(핀헤더 8의 11번)을 선택했다면 bits의 0번째 비트에 P8_11에 대한 비트 값이 담겨있다. 1이면 활성(gpio in out의 경우 출력 HIGH), 0이면 비활성(gpio in out의 경우 출력 LOW)이다.

 * @mask: bits에서 가져올 비트나 설정할 비트를 마스킹한다.


이제 ioctl를 사용하여 gpio를 제어해보자. 아래 코드는 그냥 ioctl을 저렇게 쓰는구나 정도를 표현한 코드다. (좋은 코드는 아니다. 따로 코드를 밑에 깃헙에 올려두었다.)

#include "gpio.h"

int reqfd = 0;

void init_gpio()
{
    struct gpio_v2_line_request req = {
        0,
    };
    int ret = 0;
    int fd = 0;

    ret = open("/dev/gpiochip0", O_RDWR);
    if (ret < 0)
    {
        perror("fail to init gpio");
        return;
    }

    sprintf(req.consumer, "gpio_control");       // 사용자 이름은 gpio_control
    req.config.flags = GPIO_V2_LINE_FLAG_OUTPUT; // 출력으로 설정
    req.num_lines = 1;                           // 라인의 숫자는 1개만 사용
    req.offsets[0] = 13;                         // 오프셋 13, 즉 라인 gpiochip0의 13번 선택
    req.config.num_attrs = 0;                    // 라인하나만 쓰니까 속성 설정은 안함

    fd = ret;
    ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
    if (ret < 0)
    {
        close(fd);
        perror("fail to ioctl");
        return;
    }

    close(fd); // gpiochip은 닫아도 상관없다. req.fd 로 gpio 드라이버에 ioctl 함수 요청을 날린다.
    reqfd = req.fd; 
}

void set_gpio()
{
    struct gpio_v2_line_values val = {
        0,
    };
    int ret = 0;
    val.bits = 0x01; // 최하위 비트 즉 offsets[0] 인 gpiochip0의 13번을 high로 설정
    val.mask = 0x01; // 최하위 비트 마스킹, 즉 offsets[0]만 전달됨
    ret = ioctl(reqfd, GPIO_V2_LINE_SET_VALUES_IOCTL, &val);
    if (ret < 0)
    {
        perror("fail to set gpio");
    }
}

void get_gpio()
{
    struct gpio_v2_line_values val = {
        0,
    };

    int ret = 0;
    val.mask = 0x01;                                          // 최하위 비트 마스킹, 즉 offsets[0]만 가져옴
    ret = ioctl(reqfd, GPIO_V2_LINE_GET_VALUES_IOCTL, &val); // val을 gpio 드라이버에 GET_VALUES 요청하면 val에 비트 값 담아서 줌
    if (ret < 0)
    {
        perror("fail to set gpio");
    }

    printf("gpio : %s\n", (val.bits == 1) ? "high" : "low");
}

ioctl을 사용해서 gpio를 제어하는 방법이다. 


주의할 점은 gpio high로 설정하고 어플이 끝나면, gpio 동작을 안한다. 따라서 확인해 보고 싶으면 main에서 while(1)로 무한 대기 시켜줘야 된다.


 너무 코드가 하드 코딩되어 지저분 하기도 하고,  이전 강좌에서 사용한 스레드까지 사용해서 다듬어준 코드는 아래에 깃허브 링크에 올려두었다.


https://github.com/leejugy/gpio_v2_control






3) 동작 확인


이렇게 gpio 드라이버로 제어하면 gpioinfo명령어를 쳤을 때 사용자 이름과 함께 핀 방향, 그리고 사용 여부가 나온다.


읽은 값, 쓴 값을 터미널에서 출력해준다.


실제로 read 하는 부분에 3.3V 꽂고, write 하는 부분에 led 해보면 잘 나온다.



댓글

이 블로그의 인기 게시물

[ BBB - yocto (9) ] 장치트리(DEVICE TREE)

[ BBB - yocto (11) ] uart

[ BBB - yocto (5) ] makefile 작성법과 컴파일 자동화