普通の GPIO 経由で I2C できたらなんとなく嬉しいかなと思って作ってみた。つまりソフトウェアでピン状態をいじって I2C 通信するというもので、いわゆる bit banging というやつです。
[tech] Ruby の I2C ライブラリ | Sun, Feb 16. 2014 - 氾濫原 で I2C ライブラリを書いたけど、これは i2c-dev という Linux 組込みのドライバを使ったもので、ハードウェアが持ってる I2C の機能を使うので、もしハードウェアに I2C がない場合 (普通あるけど) で、GPIO はあるという場合に使えることがあるかもしれません。
使いかた
最終的には以下のような感じで使えるようになった。もちろん Raspberry Pi には I2C ついてるのでこんなことする必要は全くない。
require "i2c/device/mpl115a2"
require "i2c/driver/gpio"
mpl = MPL115A2.new(address: 0x60, driver: I2CDevice::Driver::GPIO.new(
sda: 23, # pin 16 in raspberry pi
scl: 24, # pin 18 in raspberry pi
))
p mpl.calculate_hPa
実装
コードは以下の通りだけど、けっこう時間がかかった割にはコードサイズはそうでもない。
普通に仕様通りに実装するだけだけど、いくつか問題があった
- sysfs 経由のピン状態書きかえが非常に遅い
- C + sysfs だと5KHz 程度 (Raspberry Pi 上)
- Ruby + sysfs だと 1.2kHz 程度 (Raspberry Pi 上)
- GPIO export 直後に EACCESS (パーミッションエラー) がでる
- GPIO direction 直後での初期状態
遅い
I2C は Standard-mode でも 100kbit/s を規定してるので、全く届いていない。ただ、I2C はマスターが常に生成するクロックラインを持っていて、クロックが High のときだけデータが有効という感じで、大抵のデバイスはデータシート上では対応クロックを 0 〜 100kHz とか 0 〜 400kHz とかで書いてあるので、動くものは動くはず…… MPL115A2 というデバイスでは動いてくれた。
もちろんマスターとしてしか動かない。また、調停とかも実装してない。
ループ展開とかいろいろやったけど、多少改善はするものの、上記の通りのスピードは超えることができないので、諦めて可読性優先で富豪的に書いてある。
CPU 特化のネイティブなライブラリを使えば当然早くはなるんだけど、今回 sysfs 経由で抽象化された gpio でなければやる意味がないなと思ってやってない。
EACCESS
GPIO export 直後の EACCESS はよくわからなくて、pi を gpio グループに入れてやってるせいかもしれない。ただ、時間が経てばアクセスできるようになるので (たぶん sysfs がバグってる感じがするって人のせい的に書いといたら、カっとなって誰か調べてくれそう)、EACCESS を rescue して retry かけている。
direction 直後の状態
direction を単純に "out" に設定すると、たぶん active_low に従って初期状態が設定されると思うんだけど、うっかり SCL ラインを High にしてしまうとその後困ったことになるので、アトミックに direction 切り替え + ピンの High 又は Low 設定を行う必要がある。
しばらくわからなかったんだけど、普通に direction に対して "high" や "low" を書きこむと、初期状態が指定した状態の "out" になるみたいだった。