So-net無料ブログ作成

Raspberry Pi用pigpio Library - その1 [Raspberry Pi]

最近Gen<i>Camの実装が進まないせいで、このブログの技術的な記事が減っている。このままではこのブログの存在意義が揺らぎかねず、アイデンティティの危機を感じる(そんな大げさなものか)ので、頑張ろうと思う。

だからというわけではないけど、Raspberry Piのデジタル入出力のGPIOを使いやすくするためのライブラリであるpigpioを何回かに分けてちょっと詳しめに見ることにする。

pigpioライブラリはすごく面白い特徴を持ったライブラリなんだけど、ちょっとあさっての方向に尖ってしまってるとも言える。その一方で日本語での記事は断片的で、とりあえず動かしてみたら動きました、というような、パッと見る限りではWiringPiやbcm2835とどこが違うのかよく分からない記事が多いような気がする。本家のサイトもあまりにあっさりしていて、だからどうなんだ? ということになっているんではないだろうか。

実は僕はこれまで便利に使ってきたbcm2835ライブラリから、pigpioライブラリの比重を増やしていこうと思っている。なんで僕がそう考えたか、を説明しながら、pigpioの特徴を理解した上で使えるように、全体像が概観できる程度の、そして面白そうなところはディープに、とことん使い倒したい人の要求に耐えるようなおさらいをしてみようと思う....

0.1  Raspberry PiのGPIOアクセス

Raspberry Piを使う目的のひとつにGPIOという汎用のハードウェアアクセスが簡単にできる、というのがある。GPIOはそのSoCコアであるBCM2835チップのI/Oを直接外に出したもので、基本的にはBCM2835のI/O制御レジスタにアクセスするのがもっとも汎用的で高速な方法になっている。そのためレジスタよりはもう少し簡単にGPIOにアクセスするためのラッパがいろいろ作られている。

おそらく一番ユーザの多いのはWiringPiというライブラリで、早い時代に作られ初期のRaspberry PiユーザのLチカなんかに貢献したものである。

他にもbcm2835という名前(紛らわしい)のライブラリがあって、こっちは僕がずっと使っている。このライブラリはソースをちょっと見るだけで、いかにも組み込みプログラミングに慣れた人によるものだとわかる、非常に軽量でレイテンシも低い、使いやすいものである。

WiringPiがI2CやSPIのアクセスでは単にRaspbianのカーネルモジュールのラッパになっているのに対して、bcm2835はmmap()でレジスタを仮想アドレスに割り振ったあとは、まったくOSを経由せずにレジスタの直接アクセスで実装している。そのおかげで非常に軽いライブラリになっていて、移植しやすいのでRaspbian以外のOSでも使うことができる。

僕はこの人のシンプルなソースが気に入ってずっと使っていたけど、今年2月に1.50をリリースした後は放置プレイに突入している。そしてbcm2835ライブラリでは、例えばGPIOピンの状態の変化を知るにはポーリングしないといけなくて(それはおそらく組み込み系では普通の事態なんだろう)、たくさんのGPIOピンの変化を汎用的に追跡しようとすると、使いこなすのは難しくなっている。

そこで、bcm2835ライブラリ用に自分でOS XのCFRunLoop()みたいな汎用のポーリングループを実装しようかどうしようか、と悩んでいたとき、面白いものを知った。pigpioライブラリである。ここではこのライブラリの概要を整理したあと、それぞれの機能の使い方を簡単に紹介してみる。

1  pigpioの面白いところ

これまで全然pigpioを知らなかったんだけど、使ってみるとなかなかよくできていることがわかってきた。面白いと思えるところがいろいろある。まずいくつかの特徴をざっくりあげてみる。

1.1  コールバック関数呼び出し

pigpioではGPIOピンの状態が変化した(Low→High、あるいはHigh→Lowなど)ときにコールバック関数を呼んでもらえる。これは非常に便利で、当たり前だけどコールバックは非同期なので普段は他のことをしていていい。bcm2835や他のライブラリではできなかったのでこれだけでもpigpioを使う意味がある。

これをどうやって実現しているか、というとsignalによる割り込みではなく、ポーリング専用のスレッドを起動して、その中で名前付きパイプやsocketをpollシステムコール(selectの軽量版らしいけど、僕は使ったことないのでどう違うのかはよくわからない)で監視するようなやり方で実装されていることがわかる(普通にsignalを使うコールバックも実装されているが、これはunixのsigactionなんかのsignal handlerのラッパ)。

僕は実装の詳細までたどり着けなくて、まだちゃんと理解してないけど、割り込みを使っていないのでリソース制限なしのスレッドセーフなコールバックが実現できているらしい。面白い。

1.2  精度の高いPWM

pigpioでは精度の高いPWMをGPIOの多くのピン(0〜31ピン)で使える。bcm2835はハードウェアでPWM信号を生成できるけど、全部で2本しかなく、ロボットなんかでたくさんのサーボモータを制御したい人は、シリアル経由のPWM発信器なんかをつないでいたのが、pigpioを使えば必要なくなる。
普通のソフトウェアのループでPWMを作ると、Raspbianの場合では正確なデューティにならないし、最悪の場合パルスが途切れたりする。これはOSの仕様上仕方がない。

それをどうやって実装してるのかは僕はまだよくわからないけど、周波数は8kHzまで、5μsecの精度で出力できるらしい。普通のサーボモータを動かすには十分すぎるくらいだろう。

pigpioのPWMはほんとに超安定していて、感動を覚えるほどのもので、美しささえ感じる。サーボモータを持っている人は試してみるといい。Raspberry PiでソフトウェアPWMを実装した場合にありがちなジジジジ...という震えが一切ない。非常に面白い。

この機能がそもそもpigpioライブラリの実装動機らしくて、GPIOから最大21本のPWM信号を出してサーボモータを制御するというservoBlasterにインスパイアされたと書いている。servoBlasterはカーネルモジュールなので敷居が高いけど、pigpioはユーザランドのライブラリなので、精度が十分ならservoBlasterを使う意味はない、ということになる。

1.3  auxiliary SPI

Raspberry Pi2、3には2個目のSPIインターフェイス用のピンが出ていて、それぞれのピンをAlt4モードに設定することで使えるようになる。

ところが、raspbianのspidevカーネルモジュールやbcm2835ではサポートされていなかった。したがって2個を超えるSPIデバイスをぶら下げようとすると、CS信号のマルチプレクサの回路を用意するか、自分でチップセレクト信号を作ってGPIOの他のピンに出さなければいけなかった。

pigpioではこのふたつ目のSPIバスがauxiliary SPIとして使えるようになっている。さらにこのauxiliary SPIには3本目のチップセレクトがBCM16(40ピンヘッダの36ピン)に出ていて、何もしなくても(マルチプレクサなしに)本体だけで合計5個のSPIデバイスを接続できることになる。

これはたくさんのA/D、D/Aをつなぎたいときに便利である。例えば、A/DとD/Aはスピードが違っているのが普通なので(例えばMCP4922とMCP3208とでは最高クロックが一桁違う)、バスごとにクロックを変えて別スレッドからSPIを制御すれば、それぞれの最大のスピードで使うことができる。

1.4  bit bang

シリアル-パラレル変換はシフトレジスタで簡単に実装できるので普通はハードウェアに任される。ハードウェアで決まった以上のチャンネルが必要なときにはソフトウェアで実現するしかないが、そういうのをbit bangと言うらしい。pigpioではPWMを実現した時間精度のいいパルス発生を利用して、SPI、I2C、シリアル通信、さらには任意のデジタル波形発生(waveformと本家では呼んでいる)などを任意のGPIOピンで使えるようにしている。どれもあまり速いクロックには対応できないけど、出ているピンを好きなように使えるので、サブセットの機能で十分な場合には非常に便利である。

これらのハードウェアプロトコルのソフトウェアによるエミュレーションはunixへの実装の場合、苦労する割にはクロックが安定しなかったりして、普通は使い物にならないし、安定な実装は小細工が必要になってそれほど簡単ではない。たいしたものである。

まだ続く....
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

メッセージを送る