Raspberry PiでC言語を使ったLEDの点灯、消灯(いわいるLチカ)を試してみました。やはりGPIOの入出力に関してはこのLチカをやってみないと始まらないというところでしょうか。今回はGPIOのデバイスドライバを使った方法です。コマンドラインからのデバイスドライバを利用したLEDの点灯、消灯から、その方法をC言語から制御する方法についてです。(※どちらかというと組み込みLinux開発的な視点になっていると思いますのでご了承頂ければと思います。もっと簡単にGPIOを扱う方法も今後書くことが出来ればとは考えております。)
回路の接続
まず、ブレッドボードを使ってLEDを接続します。このページを見て頂いている方ならおなじみの方も多いと思いますが、LEDは極性があって通常は端子の長い方がアノード(プラス)で端子の短い方がカソード(マイナス)です。抵抗(220Ω)を経由してアノードをRaspberry Pi B+のGPIO21にカソードをRaspberry Pi B+のGNDにそれぞれ接続しています。抵抗の値は厳密には計算する必要がありますが、実験的には100Ω~330Ωぐらいであれば大丈夫かと思います。以下のように接続しました。(※画像はクリックで拡大表示します。その他の画像も同様です。)
GPIOの番号ですが、分かりやすくと思って上の画像で一番左の手前にしたのでGPIO21(Pin No.40)ですが、Raspberry Pi Model Bの場合は、GPIO21はなくて最後はPin No.26でGPIO7になると思います。ここからGPIO21固定で進めますのでお手数ですが必要な場合は以下等を参照して21という数字を読み換えて下さい。(※確認するのはGPIOの番号です。GPIO21、GPIO7、GPIO11等ありますが、この番号の部分です。)
raspberrypi/Raspberry PiでIO制御
RPi Low-level peripherals
今回の接続では以下がRaspberry Piの端子の部分です。
コマンドラインでの操作
以上までのように接続出来たとして、まずはコマンドラインからデバイスドライバを使ってみます。Linux系では(カーネルの)デバイスドライバは特殊ファイルとして実装されています。一般的には、/dev 配下や /sys 配下に実装されています。ここではRaspbianを使っていますが、Raspbianでは、Raspberry Pi用にGPIOのデバイスドライバが用意されていて、 /sys/class/gpio 配下で利用出来ます。この配下でファイルを扱うような操作でGPIOが操作出来ます。
実際に操作します。echo コマンドからリダイレクトでファイルに書き込む要領でデバイスドライバを操作します。(※ルート権限がなくても実行出来ます。ここではpiユーザです。)
LEDの点灯の操作です。
ポートの使用開始を指定します。(※上でも書きましたが、21はここでの番号です。必要に応じて接続したポートの番号を指定して下さい。)
$ echo 21 > /sys/class/gpio/export
ポートを出力に指定します。
$ echo out > /sys/class/gpio/gpio21/direction
ポートに1を設定します。これでLEDが点灯します。
$ echo 1 > /sys/class/gpio/gpio21/value
LEDの消灯の操作です。
ポートに0を設定します。これでLEDが消灯します。
$ echo 0 > /sys/class/gpio/gpio21/value
ポートの使用終了を指定します。
$ echo 21 > /sys/class/gpio/unexport
以上でコマンドラインからの操作は完了です。
C言語からの呼び出し
上記のコマンドラインからの実行と同様の処理をC言語でプログラミングします。以下のようなプログラムを作成しました。(※ここでもポート番号21は固定で書いています。必要な場合は書き換えて下さい。)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char **argv) { int fd = 0; //GPIO21の使用開始を指定する fd = open("/sys/class/gpio/export", O_WRONLY); if (fd < 0) { printf("GPIO export open error.\n"); exit(1); } write(fd,"21",3); close(fd); //0.1秒待つ(次のオープンでエラーになった場合があったため) usleep(100000); //GPIO21を出力に指定する fd = open("/sys/class/gpio/gpio21/direction", O_WRONLY); if (fd < 0) { printf("GPIO direction open error.\n"); exit(1); } write(fd,"out",4); close(fd); //GPIO21に1を出力する(LED ON) fd = open("/sys/class/gpio/gpio21/value", O_WRONLY); if (fd < 0) { printf("GPIO value open error.\n"); exit(1); } write(fd,"1",2); //3秒待つ usleep(3 * 1000 * 1000); //GPIO21に0を出力する(LED OFF) write(fd,"0",2); close(fd); //GPIO21の使用終了を指定する fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd < 0) { printf("GPIO unexport open error.\n"); exit(1); } write(fd,"21",3); close(fd); return 0; }
動作的にはGPIO21を3秒間ONにしてOFFにして終了すると動作です。コンパイルはRaspberry Pi上の任意の場所で保存してgccコマンドでコンパイル出来ます。例えば、gpiodev1.cというファイル名で保存したとして以下のようにコンパイル出来ます。
$ gcc gpiodev1.c -o gpiodev1
これで問題がなければ、gpiodev1という実行ファイルが出来ますので、./gpiodev1 で実行出来ます。
ここからプログラムの説明です。
ファイル操作がポイントです。Linux系のOSでは(カーネルの)デバイスドライバは特殊ファイルとして実装されると書きました。つまりファイルのように扱えるためデバイス独自の仕様を意識しないでファイル操作としてデバイスを扱うことが出来ます。Raspberry PiのGPIOではポート使用の開始から、入出力の設定、ON/OFFの設定までをファイルへの書き込みという処理で実行出来ます。
コマンドラインではリダイレクトの1行で実行したことをopen()、write()、close()の関数を使って処理をしています。それぞれの関数はLinuxのシステムコールです。Linuxのカーネルを直接呼び出しています。
参考までにですが、ファイル操作といいますと、C言語を学習する際にもよく出てくる標準ライブラリのfopen()、fclose()、fputs()等がありますが、これらの関数で同様の処理をすると期待通りに動作しなかったです。想像のレベルですが、これらの標準ライブラリのファイル操作では意識しないとバッファリングが有効になるため書き込みが即座に実行されないで変な動作になったと思います。デバイスドライバを操作する場合は、今回のようにシステムコールを使うのが間違いがないと思います。
今回はここまでです。次回は同様の処理ですが、レジスタを直接操作する方法についてです。
Linuxのデバイスドライバについて補足
Linuxのデバイスドライバについて簡単にですが補足です。
Linuxに限らずデバイスドライバといえば各種デバイス(コンピュータの周辺機器)を制御するためのプログラムであり、実際の機器の制御を隠蔽して抽象化して扱えるようになっています。(Windowsですとデバイスマネージャから管理出来ます。)
それではLinuxでのデバイスドライバについて主な特徴と思われる項目を以下に列挙します。
・カーネルのモジュールとしてカーネル空間で動作する。
・ユーザ空間とはデバイスファイルを経由してデータのやり取りを行う。
・デバイスファイルにはいくつかの種類がある。
各特徴について説明です。
カーネル空間(カーネルランド)とユーザ空間(ユーザランド)
Linuxには上記2つの(メモリ)空間が存在します。カーネル空間は、カーネル(OSの中核)が利用する空間でユーザ空間はユーザアプリケーションが利用する空間です。
2つの空間が存在する主要な理由は独立性です。ユーザアプリケーションが不正な動作をしてもカーネル空間には影響がないというメリットが考えられます。
カーネルモジュール
カーネル空間で動作するモジュール(プログラム)です。Linuxのデバイスドライバは通常はこのカーネルモジュールとして実装されています。カーネルモジュールという仕様の元に実装します。ユーザプログラミングとはまた違った実装方法になります。
デバイスファイルの種類
本文でも少し書きましたが、伝統的には/dev配下があります。今回のRaspberry PiのGPIOでは、sysfsという仕様で/sysに実装されます。このsysfsは比較的新しい仕様で、データのやり取りの他に設定にも使われます。(※今回では、指定のGPIOポートを出力に設定するという操作を行っています。)
今後、Linuxのデバイスドライバの実装方法についても書いていきたいと思いますが今回は補足ということで書きました。