🐧 Linux 総合学習プラットフォーム
実機制御 GPIO/I2C/SPI・RT ・ 上級

GPIOの基本と制御(libgpiod)

GPIOは基板上のピンを汎用の入出力として使う仕組みで、LEDの点灯やボタンの読み取りといった実機制御の土台になります。現在のLinuxでは libgpiod が標準で、入力読み取りは gpioget、出力設定は gpioset を使い、ピンは「チップ名(gpiochip0など)とライン番号」の組で指定します。古い sysfs 方式と違い、プロセス終了時にラインが自動解放されるため衝突や状態の取り残しが起きにくいのが利点です。まずは gpiodetect でチップを確認し、gpioinfo で各ラインの用途や予約状況を把握してから操作します。

基板の縁に並んだピンを、ソフトウェアから1本ずつ「電圧を出す/読む」ために使えるようにした仕組みがGPIO(General Purpose Input/Output、汎用入出力)です。LEDを光らせる、押しボタンの状態を読む、リレーやブザーを叩く——こうした実機制御のいちばん基礎にあたります。GPIOはあくまで「デジタル」の入出力で、ピンの状態はHIGH(Raspberry Piなら3.3V)かLOW(0V=GND)の2値しかありません。出力に設定したピンはこちらから電圧を与える側になり、入力に設定したピンは外からかかる電圧を読み取る側になります。アナログ値(中間の電圧)はそのままでは扱えないため、明るさを連続的に変えたいときは後述のPWMやAD/DAコンバータと組み合わせます。汎用、という名前のとおり、特定の用途に固定された専用ピンとは違い、入力にも出力にもソフトウェアの設定しだいで切り替えられるのが特徴です。

ここで最初に押さえたいのが、GPIOピンには2つの番号体系があるという点です。基板のヘッダに物理的に並んだ順の「物理ピン番号(1〜40)」と、SoC内部の論理的な「BCM番号」は別物で、libgpiodや多くのツールはこのBCM番号に対応する『ライン番号』で操作します。さらにLinuxはGPIOを「チップ+ライン」の組で管理し、チップは gpiochip0 のようなキャラクタデバイスとして /dev に現れます。つまり「gpiochip0 の 17番ライン」という指定の仕方になります。どのピンがどのラインかは環境(Pi 4とPi 5でも異なる)で変わるので、配線の前に必ず後述のツールで対応を確かめます。たとえばRaspberry Pi 5では拡張ヘッダを管理するチップ名や番号付けが従来機と変わっているため、「以前のPiで動いた数字をそのまま流用したら別のピンを叩いていた」という取り違えが起きがちです。番号は環境ごとに確認する、を鉄則にしてください。

現在の標準は libgpiod

今のLinuxでGPIOを触る標準的な方法が libgpiod です。これは /dev/gpiochipN というキャラクタデバイス経由でラインを操作するライブラリと、付属のコマンド群(gpiodetect・gpioinfo・gpioget・gpioset など)からなります。最大の利点は、ラインを使っているプロセスが終了すると、確保していたラインが自動的に解放されることです。後述する旧来のsysfs方式では、スクリプトが途中で落ちるとピンが出力のまま取り残され、次の操作とぶつかる事故が起きがちでした。libgpiodは「誰がそのラインを握っているか」をカーネルが管理するため、こうした状態の取り残しや二重確保が起きにくく、複数プログラムが同じピンを奪い合う危険も減ります。

まずは全体像の把握から始めます。gpiodetect を打つと、システムに見えているGPIOチップの一覧(チップ名・ラベル・ライン本数)が表示されます。続いて gpioinfo gpiochip0 のようにチップを指定すると、各ラインの番号・名前・現在の向き(input/output)・状態、そして「すでに別の用途で予約(used)されているか」までが一覧できます。たとえばI2CやUARTに割り当て済みのラインはここで used と表示され、勝手に使うと衝突します。配線前にこの gpioinfo で空いているラインと予約済みのラインを見分けておくのが、事故を防ぐ第一歩です。

読む gpioget、書く gpioset

入力を読むのが gpioget です。新しめのlibgpiod(v2系)では `gpioget -c gpiochip0 17` のように「どのチップの何番ラインか」を指定し、そのラインが今 0(LOW)か 1(HIGH)かを返します(v1系では `gpioget gpiochip0 17` と書きます)。ボタンを押したかどうかを読むなら、この値を繰り返し確認します。出力するのが gpioset で、`gpioset -c gpiochip0 17=1` でラインをHIGHに、`=0` でLOWにします。LEDをGPIO17につないでおけば、これだけで点灯・消灯ができます。コマンド版のgpiosetは実行している間だけラインを保持し、終了するとラインを手放す点に注意してください。点けっぱなしにしたい、あるいは連続して波形を作りたい場合は、保持し続けるオプションを使うか、Cやpythonのライブラリでラインを開いたまま制御します。

入力ピンで重要になるのがプルアップ/プルダウンの設定です。ボタンのように「押していないときはどこにもつながっていない」入力は、放っておくと電位が定まらず、ノイズでHIGHともLOWともつかない値を拾ってしまいます。これを防ぐため、内部の抵抗で既定値をHIGH(プルアップ)またはLOW(プルダウン)に固定します。libgpiodでは gpioget/gpioset に `--bias=pull-up` のようなバイアス指定があり、外付け抵抗なしで内部プルを有効にできます。たとえば「押すとGNDに落ちる」ボタンならプルアップを指定し、通常は1、押すと0、と読むのが定石です。

実務での使いどころ

実務では、まず gpiodetect と gpioinfo で地図を作り、空きラインにLEDやボタンを割り当て、gpioget/gpioset で動作確認をしてから、本番のプログラム(C言語の libgpiod API や Python バインディング)に落とし込む、という流れが基本になります。手元での切り分けにコマンド版が、製品の常駐制御にライブラリ版が向きます。コマンドで一度でも点灯・読み取りができていれば、その後コードが動かなくても「配線は正しい、原因はソフト側」と切り分けられるため、この順番には大きな意味があります。

配線の注意として、GPIOは3.3V系であり、5Vを直接入力ピンに加えるとSoCを破壊します。外部の5V論理のセンサをつなぐときはレベル変換回路を挟みます。出力電流もピンあたり十数mA程度(ピン全体の合計にも上限がある)と小さいため、モータや多数のLEDを直接駆動せず、トランジスタやMOSFET、専用のドライバICを挟んで、電力は別系統から供給します。LEDを1個つなぐときも、過電流で焼かないよう必ず電流制限抵抗を直列に入れます。これらは「動くかどうか」ではなく「壊さないかどうか」の話で、コマンドの使い方以前に押さえるべき前提です。GPIO・libgpiod・プルアップ/プルダウンの3点と、3.3V系という電気的な約束を守れば、実機制御の土台はおおむね固まります。

この項目に出てくる用語

GPIOじーぴーあいおー
汎用入出力ピン。LED点灯やボタン読み取りなど実機制御の土台。
libgpiodりぶじーぴーアイオーディー
現在標準のGPIO操作ライブラリ/ツール群(gpioget/gpioset等)。
プルアップ/プルダウンぷるあっぷぷるだうん
入力ピンを既定でHIGH/LOWに固定する抵抗設定。

関連コマンド

gpiodetectgpioinfogpiogetgpioset

▶ 学習アプリでこの続きを学ぶ・演習する