1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ラズパイ5でGPIOをC言語で操作するLibrary - libgpiommem

Last updated at Posted at 2025-03-10

📌 はじめに

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 制御 失敗 pigpiodRaspberry 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に含まれるプログラムです。
pinctrl
先に上げた、方法とは違いラズパイ5でも動作します。
ところが、あくまでLinux commandですから、Cからsystem()で呼び出しても、速度は出ません。でも pinctrl内部では、mmem()を使ってkernel memryを直に操作しているので、その操作をそっくり真似てGPIOを制御できるはずです。

📌 手順

https://github.com/raspberrypi/utils
上記からソース一式をいただき展開します。
使用するのは utils/pinctrl だけです。

bash
cd utils
cp -r pinctrl libgpiommem
cd libgpiommem

📌 Makefile を用意する

utilはCmakeで管理されていますが、単一のlibraryなのでmakeで独立に管理します。

Makefile
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

📌 公開

本来は、ここで試験を行いますが、今回試験済みなので後回しにして公開します。
マシンの別の場所からも参照できるようになります。

bash
sudo make install
echo "/usr/local/lib" | sudo tee -a /etc/ld.so.conf.d/libgpiommem.conf
sudo ldconfig

📌 試験プログラムを書きます

任意の場所に作業用Dirを作って移動します。

bash
mkdir gpiommem
cd gpiommem

📌 はじめはLチカ

サンプルプログラムを書きます。

t_gpio_out.c
// 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をつくります。

Makefile
all: t_gpio_out

t_gpio_out: t_gpio_out.c
	gcc -o t_gpio_out t_gpio_out.c -lgpiommem

makeします。

bash
make
sudo ./t_gpio_out

📌 動作試験

GPIO21にLED+330ΩをつけLチカを確認してください。
usleep(50000)を調整すると周期が変わります。取ってしまったら、ocillo scopeでみて約20MHzでした。
20MHz.png

📌 入出力試験

先立って、Libの開発とサンプルの開発を分けましょう。

bash
mkdir ../gpiommem_test
cp t_libgpiommen.c ../gpiommem_test
cd ../gpiommem_test

📌 入出力試験

出力を確かめたので、入力も試験します。

t_gpio_io.c
// 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を改造します。

Makefile
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

bash
make
sudo ./t_gpio_io

GPIO20とGPIO21をショートするとリングカウンターになり発振します。周波数は225kHzでした。
225kHz.png

📌 まとめ

⚡ 他の従来の方法 (sysfs, libgpiod, /dev/gpiomem, pigpio, rpio) はすべて動作せず!
🔧 最終的に pinctrl をハックし、libgpiommap を構築することで問題を解決!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?