GPIO 조작 방법(OrangePi5 예시)

1. PinMap

GPIO, 그게 뭔가요?

GPIOGeneral Purpose Input/Output의 약자로, 번역하면 ‘범용 입출력 포트’.
말 그대로 “특별한 용도가 정해지지 않은” 핀(pin)들을 뜻합니다.
개발자가 원하면 입력(Input) 으로 써서 버튼·센서 값을 읽을 수도, 출력(Output) 으로 써서 LED 켜기, 모터 돌리기, 스피커 울리기도 가능합니다.

쉽게 말해, 마이크로컨트롤러의 ‘멀티탭’ 같은 존재죠.

2. G.PIO 상태 확인

sudo apt install gpiod
sudo apt install python3-libgpiod
gpio readall
sudo cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pins

3. python 코드

GPIO4_A3을 컨트롤 한다고 할 시
a. gpio readall 로 해당 핀의 GPIO행 값을 보면 131이 보인다.

b. sudo cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pins
하면 pin 131 값을 보면
pin 131 (gpio4-3) 3:gpio4 임을 알 수 있다.

c. 최종 아래 코드가 된다 즉
chip = gpiod.Chip(“gpiochip4”)
line = chip.get_line(3)

import gpiod
import time

chip = gpiod.Chip("gpiochip4")
line = chip.get_line(3)
line.request(consumer="test", type=gpiod.LINE_REQ_DIR_OUT)

while True:
    line.set_value(1)
    time.sleep(1)
    line.set_value(0)
    time.sleep(1)

4. C 코드

sudo apt update
sudo apt install gpiod libgpiod-dev

libgpiod 라이브러리 사용

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>

#define CONSUMER "my-gpio-app"
#define GPIO_CHIP "/dev/gpiochip4"  // bank4에 해당
#define GPIO_LINE 3                 // A3 = 그룹 A, 오프셋 3

int main(void) {
    struct gpiod_chip *chip;
    struct gpiod_line *line;
    int ret;

    // GPIO 칩 열기
    chip = gpiod_chip_open(GPIO_CHIP);
    if (!chip) {
        perror("Open chip failed");
        return 1;
    }

    // 라인 얻기 (A3)
    line = gpiod_chip_get_line(chip, GPIO_LINE);
    if (!line) {
        perror("Get line failed");
        gpiod_chip_close(chip);
        return 1;
    }

    // 출력으로 요청
    ret = gpiod_line_request_output(line, CONSUMER, 0);
    if (ret < 0) {
        perror("Request line as output failed");
        gpiod_chip_close(chip);
        return 1;
    }

    // 토글 테스트
    for (int i = 0; i < 10; i++) {
        gpiod_line_set_value(line, 1);
        printf("GPIO set HIGH\n");
        sleep(1);

        gpiod_line_set_value(line, 0);
        printf("GPIO set LOW\n");
        sleep(1);
    }

    gpiod_line_release(line);
    gpiod_chip_close(chip);
    return 0;
}

컴파일& 실행

gcc -o gpio_test gpio_test.c -lgpiod
sudo ./gpio_test

보다 빠른 제어 필요 시

– 접근 방식별 속도 비교

방식장점단점속도
sysfs (/sys/class/gpio)단순, 예제 많음deprecated, 커널과 유저 공간 오버헤드 큼수 kHz 수준
libgpiod (gpiod_line_set_value)표준적, 안전시스템콜 호출 오버헤드 존재수십~수백 kHz
mmap(직접 메모리 접근, /dev/mem)가장 빠름 (레지스터 직접 접근)코드 복잡, 보드별 레지스터 오프셋 필요MHz 단위 가능

mmap(직접 메모리 접근, /dev/mem) C코드

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>

#define GPIO4_BASE fec50000   // RK3588 GPIO4 베이스 주소 (예시, DTB에서 확인 필요)
#define GPIO_SIZE  0x10000

#define GPIO_SWPORTA_DDR   0x0004   // direction
#define GPIO_SWPORTA_DR    0x0000   // data register

int main() {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    volatile uint32_t *gpio;
    gpio = mmap(NULL, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO4_BASE);
    if (gpio == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // A3 = bit 3
    int bit = 3;

    // 방향 설정: output
    gpio[GPIO_SWPORTA_DDR/4] |= (1 << bit);

    // 빠른 토글
    for (int i = 0; i < 1000000; i++) {
        gpio[GPIO_SWPORTA_DR/4] |= (1 << bit);   // HIGH
        gpio[GPIO_SWPORTA_DR/4] &= ~(1 << bit);  // LOW
    }

    munmap((void*)gpio, GPIO_SIZE);
    close(fd);
    return 0;
}

RK3588 GPIO4 베이스 주소 확인하는법 (예시, DTB에서 확인)

cd /boot/dtb-6.1.99-rockchip-rk3588/rockchip/
rk3588s-orangepi-5.dtb

dtc -I dtb -O dts -o rk3588s-orangepi-5.dts rk3588s-orangepi-5.dtb

DTS파일 확인 시

                gpio@fec50000 {
                        compatible = "rockchip,gpio-bank";
                        reg = <0x00 0xfec50000 0x00 0x100>;
                        interrupts = <0x00 0x119 0x04>;
                        clocks = <0x02 0x83 0x02 0x84>;
                        gpio-controller;
                        #gpio-cells = <0x02>;
                        gpio-ranges = <0x1a8 0x00 0x80 0x20>;
                        interrupt-controller;
                        #interrupt-cells = <0x02>;
                        phandle = <0x107>;
                };

reg = <0x00 0xfec50000 0x00 0x100>;

  • 베이스 주소 = 0xfec50000
  • 사이즈 = 0x100
root@orangepi5:/boot/dtb-6.1.99-rockchip-rk3588/rockchip# grep -n "gpio@" rk3588s-orangepi-5.dts
22:             gpio0 = "/pinctrl/gpio@fd8a0000";
23:             gpio1 = "/pinctrl/gpio@fec20000";
24:             gpio2 = "/pinctrl/gpio@fec30000";
25:             gpio3 = "/pinctrl/gpio@fec40000";
26:             gpio4 = "/pinctrl/gpio@fec50000";
7862:           gpio@fd8a0000 {
7875:           gpio@fec20000 {
7888:           gpio@fec30000 {
7901:           gpio@fec40000 {
7914:           gpio@fec50000 {
11493:          gpio0 = "/pinctrl/gpio@fd8a0000";
11494:          gpio1 = "/pinctrl/gpio@fec20000";
11495:          gpio2 = "/pinctrl/gpio@fec30000";
11496:          gpio3 = "/pinctrl/gpio@fec40000";
11497:          gpio4 = "/pinctrl/gpio@fec50000";

또는

root@orangepi5:/boot/dtb-6.1.99-rockchip-rk3588/rockchip# cat /proc/iomem | grep gpio
fd8a0000-fd8a00ff : fd8a0000.gpio gpio@fd8a0000
fec20000-fec200ff : fec20000.gpio gpio@fec20000
fec30000-fec300ff : fec30000.gpio gpio@fec30000
fec40000-fec400ff : fec40000.gpio gpio@fec40000
fec50000-fec500ff : fec50000.gpio gpio@fec50000

By byun

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다