はじめに
ラズパイでGPIOを操作する方法はたくさんありますが、その大部分がラズパイ5では動かないという驚愕の現実がありCで操作する、libgpiommemを制作し公開しました。
少なからず反響があり、「Pythonでもこれを使いたい」という声があったため、Wrapperを作りましたので、公開します。
簡単な使い方
- libgpiommem.soを書いてある手順で展開しinstallします。
- ここに書いた手順でPyGpioMmenをDLします。
- サンプルプログラムを試験します。
GitLabからのFile取得
簡単に使えるようGitLabにもファイルを置きました。ご利用ください。
PyGpioMmem本体
python
# MIT License.
# Copy right 2025 by Shigeru Makino.
import ctypes
# ライブラリのロード
libgpiommem = ctypes.CDLL("libgpiommem.so")
# 戻り値の型を設定
libgpiommem.gpiolib_init.restype = ctypes.c_int
libgpiommem.gpiolib_mmap.restype = ctypes.c_int
libgpiommem.gpio_set_fsel.argtypes = [ctypes.c_uint, ctypes.c_int] # `GPIO_FSEL_T` は `int`
libgpiommem.gpio_set_pull.argtypes = [ctypes.c_uint, ctypes.c_int]
libgpiommem.gpio_set_drive.argtypes = [ctypes.c_uint, ctypes.c_int]
libgpiommem.gpio_get_level.argtypes = [ctypes.c_uint]
libgpiommem.gpio_get_level.restype = ctypes.c_int
# GPIO 定数
PULL_NONE = 0
PULL_UP = 1
PULL_DOWN = 2
DRIVE_LOW = 0
DRIVE_HIGH = 1
# `GPIO_FSEL_INPUT` と `GPIO_FSEL_OUTPUT` をハードコード(C の定義に基づく)
GPIO_FSEL_INPUT = 16 # `0x10` の値
GPIO_FSEL_OUTPUT = 17 # `GPIO_FSEL_INPUT + 1`
class GPIO:
def __init__(self):
""" GPIOライブラリの初期化 """
ret = libgpiommem.gpiolib_init()
if ret < 0:
raise RuntimeError("Failed to initialize gpiolib")
ret = libgpiommem.gpiolib_mmap()
if ret < 0:
raise RuntimeError("Failed to mmap gpiolib")
def set_mode(self, pin, mode):
libgpiommem.gpio_set_fsel.argtypes = [ctypes.c_uint, ctypes.c_int] # `GPIO_FSEL_T` は int
libgpiommem.gpio_set_fsel.restype = ctypes.c_int # 戻り値を整数に設定
ret = libgpiommem.gpio_set_fsel(ctypes.c_uint(pin), ctypes.c_int(mode))
return ret
def set_pull(self, pin, pull):
""" GPIO のプル設定 """
libgpiommem.gpio_set_pull(pin, pull)
def write(self, pin, value):
""" GPIO の出力を設定 """
libgpiommem.gpio_set_drive.argtypes = [ctypes.c_uint, ctypes.c_int] # 型を明示
libgpiommem.gpio_set_drive.restype = None # C 側で `void` の場合
libgpiommem.gpio_set_drive(ctypes.c_uint(pin), ctypes.c_int(value))
def read(self, pin):
""" GPIO の入力を取得 """
return libgpiommem.gpio_get_level(pin)
原理と問題点
ctypes を実直に使っています。
C言語側がenumで引数を使っているので、Pythonと齟齬が生じ面倒なのでdefineしてます。大本のpinctrl command で変更があると、追従できません。
Sampleと使い方
blink.py
出力試験です。高速のLチカです。
python
import pygpiogpmem as gpmem
gpio = gpmem.GPIO()
gpio21 = 21
# GPIO の設定
gpio.set_mode(gpio20, gpmem.GPIO_FSEL_INPUT)
gpio.set_pull(gpio20, gpmem.PULL_UP)
gpio.set_mode(gpio21, gpmem.GPIO_FSEL_OUTPUT)
# 初期化チェック
print("GPIO initialized successfully!")
while True:
gpio.write(gpio21, gpmem.DRIVE_HIGH)
gpio.write(gpio21, gpmem.DRIVE_LOW)
実行
bash
sudo python3 blink.py
GPIO21に矩形波が出ます。
最大応答速度を計測するため、システムに負荷がかかります。本来はusleep(1)を挟んで、調整してください。
オシロで測って430kHzが出ました。
ring.py
python
import pygpiommem as gpmem
gpio = gpmem.GPIO()
gpio20 = 20
gpio21 = 21
# GPIOの設定
gpio.set_mode(gpio20, gpmem.GPIO_FSEL_INPUT)
gpio.set_pull(gpio20, gpmem.PULL_UP)
gpio.set_mode(gpio21, gpmem.GPIO_FSEL_OUTPUT)
gpio.write(gpio21, gpmem.DRIVE_LOW)
while True:
if gpio.read(gpio20) == 0:
gpio.write(gpio21, gpmem.DRIVE_HIGH)
else:
gpio.write(gpio21, gpmem.DRIVE_LOW)
実行
bash
sudo python3 ring.py
GPIO20,GPIO21をショートするとリング発振器になります。
周波数は133kHzでした。
まとめ
- libgipimmem にラッパーをかけ python で使えるようにしました
- 簡単にPythonでGPIO入出力ができます