Linuxの流儀でGPIOを監視する

Raspberry PiのGPIOにスイッチをつないでその状態を読み取る実験。これがもしArduinoだったらポートを読み続ければいいのだけれど、Raspberry PiはLinuxの流儀でやる必要がある。手短に言うと時間を長く占有してはいけない。Linuxは難しいことを比較的簡単に実現するかわりに簡単なことを比較的難しくする。

gpio_interrupt_view

先達のやりかたを見るとWiringPiやbcm2835ライブラリでピンに割り込みを設定する例が多い。この場合、割り込み関数で頑張ればいろいろな処理ができる。でも、ピンの変化を捉えるだけなら/sys/class/gpio/ピンの名前/edgeにrisingかfallingかbothを書き込むのが手っ取り早い。割り込み関数を呼び出さないから正確には「イベントを発生するだけ」かも知れないが、似たようなもの。そのイベントはpoll関数(システムコール)で検出できる。

スイッチがGPIO25につながっているとするとソースはこんな感じ。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>

int main(){
int i;
int fd;
int ret;
struct pollfd pfd;
char c;

//Enable gpio25
fd = open(“/sys/class/gpio/export”, O_WRONLY);
write(fd, “25”, 2);
close(fd);

//Set gpio25 as input
fd = open(“/sys/class/gpio/gpio25/direction”, O_WRONLY);
write(fd, “in”, 2);
close(fd);

//Set gpio25 interrupt
fd = open(“/sys/class/gpio/gpio25/edge”, O_WRONLY);
//write(fd, “falling”, 7);
write(fd, “both”, 4);
close(fd);

//Wait for interrupt, repeat 10 times.
fd = open(“/sys/class/gpio/gpio25/value”, O_RDONLY);
pfd.fd = fd;
pfd.events = POLLPRI;
for(i = 0; i < 10; i++){
lseek(fd, 0, SEEK_SET);
ret = poll(&pfd, 1, 3000);
read(fd, &c, 1);
if(ret == 0)
printf(“Timeout\n”);
else
printf(“%d=%c\n”, i, c);
}
close(fd);

//Disable gpio25
fd = open(“/sys/class/gpio/unexport”, O_WRONLY);
write(fd, “25”, 2);
close(fd);

return(0);
}

普通にビルドして管理者の権限で実行。
cc pin.c -o pin
sudo ./pin

実行結果はこんな感じ。

gpio_interrupt

スイッチを押すと0を表示し、離すと1を表示し、何もしないと3秒でTimeoutを表示する。読み取りを10回実行したら終了。ただそれだけのソースに丸1日かかった。原因のひとつはManual pageのpoll関数の説明。timeout引数はブロックする最小時間と書いてある。こちらの日本語訳も同じ。これって最大時間の間違いだよね。

gpio_poll_man

マニュアルは真面目に読むタチだが、マニュアル人間じゃダメってことか。

広告
カテゴリー: RaspBerryPi パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中