📌 はじめに
Raspberry Pi 5 のGPIOを扱う場合、これまでのModel A/B/3 ...の手法は使えず、かと言って現時点(2025/03/10)まとまった正規ドキュメントがなく、手探り状態でした。
そこで手がかりのあった、pinctrl commandをハックしてC言語からGPIOを操作し、ついでそれをもとにmmemを直接操作するライブラリ libgpiommem.soを制作し公開します。
📌 これまでの方法
これまで試行錯誤した Raspberry Pi 5 の GPIO 操作手法と、その結果 (成功/失敗) をまとめます。
📌 Raspberry Pi 5 GPIO 操作の試行錯誤まとめ
方法 | 目的 | 結果 | 理由・問題点 |
---|---|---|---|
sysfs GPIO (/sys/class/gpio/ ) |
echo 21 > /sys/class/gpio/export で GPIO21 を制御 |
❌ 失敗 |
Raspberry Pi 5 では sysfs GPIO が無効化されている (カーネル仕様変更) |
libgpiod (gpioset , gpioinfo ) |
libgpiod を使用し、GPIO21 を ON/OFF |
❌ 失敗 |
gpiochip571 などが gpioinfo に表示されない (/dev/gpiochipX が見つからない) |
pinctrl を使った手動制御 (pinctrl set 21 hi ) |
pinctrl コマンドで GPIO を制御 |
✅ 部分成功 | GPIO のモード (op ) は変更できたが、mmap との連携が不完全
|
/dev/mem + mmap (devmem2 を使ったレジスタ操作) |
devmem2 で GPIO のレジスタを直接変更 |
❌ 失敗 | Raspberry Pi 5 では レジスタ構成が異なる (RP1-PIO ) ため、従来の devmem2 では動作せず |
/dev/gpiomemX (gpiomem0 ~ gpiomem4 ) |
/dev/gpiomemX を使って GPIO 制御 |
❌ 失敗 | Raspberry Pi 5 では /dev/gpiomemX が使えない (/dev/gpiomem 自体が非推奨) |
gpiod_ctxless_set_value() (libgpiod の C API) |
gpiod_ctxless_set_value() で GPIO を制御 |
❌ 失敗 |
gpiod_chip_open_by_name("gpiochip571") がエラー (No such file or directory ) |
pigpio (pigpiod デーモンを使った GPIO 操作) |
pigpio を使って GPIO 制御 |
❌ 失敗 |
pigpiod が Raspberry Pi 5 に対応していない (gpioHardwareRevision: unknown rev code ) |
rpio (rpio ライブラリを使った GPIO 制御) |
rpio で GPIO を制御 |
❌ 失敗 |
rpio が Raspberry Pi 5 に未対応 (E: パッケージ rpio が見つかりません ) |
pinctrl のソースコードをハック |
pinctrl のコードを修正し、GPIO の制御を可能に |
✅ 成功! | GPIO を mmem で直接操作する方法を確立! (libgpiommem ) |
mmem を使った GPIO 高速制御 (libgpiommem ) |
mmem で GPIO レジスタを直接操作 |
✅ 成功! |
15MHz 以上の高速 GPIO 制御に成功 (libgpiommem としてライブラリ化) |
正に、死屍累々、試してはNG、試してはNGの繰り返して疲弊すました。
📌 pinctrl とは
raspberry pi utilsに含まれるプログラムです。
先に上げた、方法とは違いラズパイ5でも動作します。
ところが、あくまでLinux commandですから、Cからsystem()で呼び出しても、速度は出ません。でも pinctrl内部では、mmem()を使ってkernel memryを直に操作しているので、その操作をそっくり真似てGPIOを制御できるはずです。
📌 手順
https://github.com/raspberrypi/utils
上記からソース一式をいただき展開します。
使用するのは utils/pinctrl だけです。
cd utils
cp -r pinctrl libgpiommem
cd libgpiommem
📌 Makefile を用意する
utilはCmakeで管理されていますが、単一のlibraryなのでmakeで独立に管理します。
CC = gcc
CFLAGS = -O2 -Wall -fPIC
LDFLAGS = -shared
PREFIX = /usr/local
LIB_NAME = libgpiommem
SRC_FILES = util.c gpiolib.c gpiochip_bcm2835.c gpiochip_bcm2712.c gpiochip_rp1.c
OBJ_FILES = $(SRC_FILES:.c=.o)
HEADER_FILES = gpiolib.h
all: libgpiommem.so
libgpiommem.so: $(OBJ_FILES)
$(CC) $(LDFLAGS) -o $@ $(OBJ_FILES)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
install:
install -d $(PREFIX)/lib $(PREFIX)/include
install -m 755 libgpiommem.so $(PREFIX)/lib/
install -m 644 $(HEADER_FILES) $(PREFIX)/include/
clean:
rm -f *.o *.so
📌 公開
本来は、ここで試験を行いますが、今回試験済みなので後回しにして公開します。
マシンの別の場所からも参照できるようになります。
sudo make install
echo "/usr/local/lib" | sudo tee -a /etc/ld.so.conf.d/libgpiommem.conf
sudo ldconfig
📌 試験プログラムを書きます
任意の場所に作業用Dirを作って移動します。
mkdir gpiommem
cd gpiommem
📌 はじめはLチカ
サンプルプログラムを書きます。
// MIT License.
// Copy right 2025 by Shigeru Makino.
#include <stdio.h>
#include <unistd.h>
#include <gpiolib.h>
#define LED_GPIO 21 // LED 用 GPIO
int main() {
int ret;
ret = gpiolib_init();
if(ret < 0){
printf("fail gpiolib_init = %d\n", ret);
return -1;
}
ret = gpiolib_mmap();
if(ret < 0){
printf("fail gpiolib_mmap = %d\n", ret);
return -1;
}
gpio_set_fsel(LED_GPIO, GPIO_FSEL_OUTPUT);
printf("Blinking LED on GPIO %d...\n", LED_GPIO);
while (1) {
gpio_set_drive(LED_GPIO, 1);
usleep(50000);
gpio_set_drive(LED_GPIO, 0);
usleep(50000);
}
return 0;
}
📌 make します
こちらにもMakefileをつくります。
all: t_gpio_out
t_gpio_out: t_gpio_out.c
gcc -o t_gpio_out t_gpio_out.c -lgpiommem
makeします。
make
sudo ./t_gpio_out
📌 動作試験
GPIO21にLED+330ΩをつけLチカを確認してください。
usleep(50000)を調整すると周期が変わります。取ってしまったら、ocillo scopeでみて約20MHzでした。
📌 入出力試験
先立って、Libの開発とサンプルの開発を分けましょう。
mkdir ../gpiommem_test
cp t_libgpiommen.c ../gpiommem_test
cd ../gpiommem_test
📌 入出力試験
出力を確かめたので、入力も試験します。
// MIT License.
// Copy right 2025 by Shigeru Makino.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "gpiolib.h"
int main()
{
int input = 0;
unsigned int gpio20 = 20;
unsigned int gpio21 = 21;
int ret;
ret = gpiolib_init();
if(ret < 0){
printf("fail gpiolib_init = %d\n", ret);
return -1;
}
ret = gpiolib_mmap();
if(ret < 0){
printf("fail gpiolib_mmap = %d\n", ret);
return -1;
}
gpio_set_fsel(gpio20, GPIO_FSEL_INPUT);
gpio_set_pull(gpio20, PULL_UP);
gpio_set_fsel(gpio21, GPIO_FSEL_OUTPUT);
gpio_set_pull(gpio21, PULL_NONE);
gpio_set_drive(gpio21, DRIVE_LOW);
while(1){
input = gpio_get_level(gpio20);
if (input == 0) {
gpio_set_drive(gpio21, DRIVE_HIGH);
}
else {
gpio_set_drive(gpio21, DRIVE_LOW);
}
}
return 0;
}
Makefilを改造します。
all: t_gpio_out t_gpio_io
t_gpio_out: t_gpio_out.c
gcc -o t_gpio_out t_gpio_out.c -lgpiommem
t_gpio_io: t_gpio_io.c
gcc -o t_gpio_io t_gpio_io.c -lgpiommem
clean:
rm t_gpio_out t_gpio_io
make
sudo ./t_gpio_io
GPIO20とGPIO21をショートするとリングカウンターになり発振します。周波数は225kHzでした。
📌 まとめ
⚡ 他の従来の方法 (sysfs, libgpiod, /dev/gpiomem, pigpio, rpio) はすべて動作せず!
🔧 最終的に pinctrl をハックし、libgpiommap を構築することで問題を解決!