
라즈베리 파이 Zero 2W에서는 GPIO(범용 입출력 핀)를 직접 조작해 초당 5,000만 회가 넘는 핀 변화를 만들 수 있었다. 표준 리눅스 GPIO 드라이버를 거치는 방식보다 훨씬 빠르다.
글쓴이는 리눅스 기반 단일 보드 컴퓨터에서 GPIO 레지스터에 직접 접근하는 방법과 그 결과를 소개했다. 드라이버가 핀 사용을 막거나 속도가 부족할 때, 다른 컨트롤러 없이 LCD나 병렬 전자잉크 화면을 구동하려는 하드웨어 개발자에게 참고가 될 수 있다.
이게 뭔데
GPIO 레지스터는 핀의 입력·출력 상태와 기능을 제어하는 메모리 공간이다. 수도꼭지 손잡이를 직접 돌리는 것에 빗대면, 드라이버를 거치지 않고 레지스터를 다루는 방식은 핀 상태를 바꾸는 제어 경로를 짧게 만든다. 다만 리눅스 사용자 프로그램은 물리 주소에 바로 접근할 수 없으므로, mmap()으로 레지스터 영역을 가상 주소 공간에 연결해야 한다.
드라이버를 우회하면 무엇이 달라지나
리눅스에서는 장치 트리 오버레이와 드라이버가 여러 사용자의 하드웨어 접근을 관리한다. 표준 GPIO 드라이버인 GPIOD는 여러 핀을 함께 다룰 수 있지만, 글쓴이는 고속 작업에서 호출과 핀 변경 사이의 코드 계층이 속도를 제한한다고 설명한다. 빠른 컴퓨터에서도 드라이버를 통한 핀 변화는 초당 약 20만 회였고, 직접 레지스터에 쓰는 효율적인 코드는 라즈베리 파이 Zero 2W에서 초당 5,000만 회 이상, 라즈베리 파이 4B에서 약 7,500만 회에 가까운 속도를 냈다.
핀의 기능 충돌도 문제다. Pimoroni Display HAT mini의 LCD는 데이터·명령 신호를 라즈베리 파이의 SPI MISO 핀인 21번에 연결한다. SPI 드라이버를 켜면 드라이버가 19·21·23번 핀을 점유해 GPIO로 쓰지 못할 수 있다. 부팅 설정 파일 /boot/config.txt에 MISO 핀을 사용하지 않도록 지정하는 방법은 구현에 따라 다르거나 제공되지 않을 수 있다. Pimoroni는 라즈베리 파이용 소프트웨어를 제공하지만, 다른 리눅스 단일 보드 컴퓨터에서는 사용자가 해결해야 한다.
메모리 주소는 어떻게 연결하나
사용자 프로그램은 가상 주소 공간에서 실행되므로, 물리 주소를 그대로 쓰면 예외가 발생할 수 있다. 글에서 예로 든 Allwinner A733의 GPIO 레지스터는 물리 주소 0x2000000에서 시작한다. mmap()에 연결할 물리 시작 주소와 영역 크기를 전달하면, 반환된 포인터를 통해 레지스터를 제어할 수 있다. 이때 /dev/mem에 접근하려면 보통 관리자 권한이 필요하며, 글쓴이는 sudo로 실행해야 한다고 설명한다.
A733에서는 GPIO 핀마다 설정 비트 4개를 할당한다. 32비트 설정 레지스터 하나가 핀 16개의 설정을 다루며, 모드 0은 입력, 모드 1은 출력이다. 글쓴이는 GPIO 포트 B부터 L까지를 8비트 값으로 표현한다. 예를 들어 포트 B의 3번 핀은 0x13이다. 코드에서 1+(pin>>4)를 쓰는 이유는 앞의 0x80바이트가 특수 레지스터에 쓰이고 GPIO 제어 레지스터는 0x100에서 시작하기 때문이다. 여러 SoC에 맞춰 코드를 공통화하려고 PORT_REG 구조체도 만들었다. 자세한 레지스터 설명은 A733 사용 설명서에 있다.
GPIO 레지스터에는 비트를 직접 쓰는 방식과 설정(set)·해제(clear) 레지스터를 쓰는 방식이 있다. 설정·해제 레지스터는 기존 값을 읽고 수정해 다시 쓰지 않아도 한 번의 명령으로 여러 비트를 바꿀 수 있다. A733은 두 방식을 모두 지원하지만, 라즈베리 파이 SoC는 설정·해제 방식만 제공한다. 글쓴이는 해당 데이터 출력 루프에서는 직접 쓰기 레지스터가 더 빠를 수 있다고 본다. 정수 비트 연산이 가상 하드웨어 주소에 쓰는 동작보다 빠를 수 있기 때문이다.
화면 구동에서 속도 차이는 어느 정도였나
글쓴이는 bb_spi_lcd 라이브러리의 사용자 지정 초기화·데이터 쓰기 기능을 이용해 OrangePi Zero 3W에서 Pimoroni Display HAT mini를 구동하는 예를 들었다. 필요한 코드는 핀 모드 설정, 디지털 쓰기, 소프트웨어로 SPI 신호를 만드는 비트뱅잉(bit-banging) 쓰기 함수다. 첫 구현은 내부 반복문에서 digitalWrite()를 호출했다. 320×240 LCD를 검정색으로 채우는 데 478ms가 걸렸고, 이는 SPI 클럭 약 2.5MHz에 해당한다.
GPIO 레지스터를 직접 다루는 방식으로 바꾸자 출력 속도는 약 6MHz에 해당했다. 글쓴이는 A733 하드웨어에서 데이터 레지스터 쓰기가 지연의 원인이라고 추정했지만, 메모리 관리 장치 때문인지 다른 이유인지는 확신하지 못했다. A733의 설정·해제 레지스터도 작동하지 않았다고 밝혔다. H618을 쓴 OrangePi Zero 2W에서도 GPIO 레지스터 쓰기에 비슷한 지연이 관찰됐다.
반면 라즈베리 파이 Zero 2W에서는 digitalWrite()를 이용한 단순한 코드가 환산 기준 약 33MHz SPI, 직접 레지스터를 조작한 빠른 코드는 약 45MHz를 냈다. 글쓴이는 이 결과가 Allwinner SoC보다 라즈베리 파이 SoC의 설정·해제 레지스터 지연이 짧다는 점을 보여준다고 해석했다. 이는 해당 글의 실험 결과이며, 여러 보드에서 확인한 일반 성능 비교는 아니다.
직접 레지스터에 접근하면 리눅스 드라이버가 제공하는 사용자 간 보호와 하드웨어 관리 경로를 우회하게 된다. 글쓴이는 이 방법을 다른 보드에서도 시험할 계획이라고 했다. 따라 해보려면 보드별 물리 주소와 레지스터 배치를 사용 설명서에서 확인하고, /dev/mem 접근 권한과 핀의 기능 충돌을 살펴야 한다. 예제 프로젝트는 GitHub 저장소, LCD 라이브러리는 bb_spi_lcd에서 볼 수 있다. 글은 여러 보드에서 재현한 성능 수치와 직접 접근 방식의 안전한 사용 범위를 밝히지 않는다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요