ちょっとググってもいい感じのドキュメントが見つからず難儀したので、Perl でやるならこれで!! って感じで、コピペして使えるようなサンプルコードを書きました

社内でちょっと喋る必要があったので資料もあります (公開しても良さそうなように資料をつくってあります)

  1. トップ
  2. tech
  3. Perl で Thrift を扱うサンプルプログラム

なんかうまく AVR だと動かなかったりしたので、カっとなってテストを充実させた。(動くようになった)

とはいえ、コード上でテストだけ書いてもよくわからないので、可視化させながらやった。つまり時系列にバスの動きをシミュレーションできるようなコードを書いた。Ruby で cairo が結構かんたんに使えたので良かった。Time.now によってグラフを書いているので、多少ゆらぎがある。しかしこうすることで、意図せず状態を変えてしまっているのが一目でわかるようになったので大変役にたった。

例えば、i2cset のテストの場合

i2cget のテストの場合

見ての通り get のほうが複雑、というのも get するためにどのアドレスを get するかを書かないといけないから……

オシロスコープでの実際の波形

テストと同じデータの送受信 (対 AVR)。波形を綺麗にするために速度を 1kHz 程度に制限して (周辺処理に時間がかかるので最終的に400Hz程度だけど) sleep をちゃんと入れるようにしている。速度は下がるけど波形は綺麗になる。時間の単位さえ気にしなければ I2C 動いてる感がある。

i2cset

i2cget

AVR での repeated start condition in slave

AVR 向けだと repeated start condition を発行したとき AVR がハングする問題があって、解決方法がわからないので stop condition を常に挟むように実装を変えた。MPL115A2 だと普通に動くんだけど……

  1. トップ
  2. tech
  3. 続・GPIO (sysfs) を使ったソフトウェア I2C

普通の 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" になるみたいだった。

  1. トップ
  2. tech
  3. GPIO (sysfs) を使ったソフトウェア I2C

Locale::Maketext::Extract::Plugin 以下にはいろいろ対応してるフォーマットがあったりする。まぁ大抵一緒なので頑張って使う必要もないけど、これらを使って .po ファイルを作らないまでも、msgid の抽出だけ行いたいという場合があったりします。そんなときは直接 LME インスタンスを作って extract_file をかけて compile すれば、とれるようになるみたいです。

use Locale::Maketext::Extract;
use Locale::Maketext::Extract::Plugin::Xslate;
use File::Zglob;

my $lme = Locale::Maketext::Extract->new(
    plugins => {
        perl => [ 'pm' ],
        xslate  => {
            syntax     => 'TTerse',
            extensions => [qw/ tt /],
        },
        generic => [ 'js' ]
    },
    warnings => 1,
    verbose => 0,
);
 
 
for (zglob('lib/**/*.pm'), zglob('template/**/*.tt'), zglob('static/**/*.js')) {
    $lme->extract_file($_);
}

$lme->compile(1); # これをしないと msgids がとれない

for my $msgid ($lme->msgids) {
    say $msgid;
    $lme->msg_positions($msgid); # 見つかった場所がとれる
}
  1. トップ
  2. tech
  3. Locale::Maketext::Extract でスキャンだけする。
  1. トップ
  2. perl
  3. Locale::Maketext::Extract でスキャンだけする。

Ruby から Linux の i2c-dev 経由でデバイスを操作するライブラリを適当に書いた。

比較的汎用的な I2CDevice クラスと、サブクラスとして特定デバイス用のものをいくつかまとめた。汎用クラスはそんな重要ではなくて、特定デバイスの操作を纏めるのを主な目的とする感じ……

デバイスごとのドライバを必要になり次第書いておいていく感じにするつもりなので (そんなに増えないと思うけど)、rubygems としてはアップロードしてない。

GPIO でのインターフェイスを追加して、gem にあったほうが便利な気がしてきたので、gem push しました。https://rubygems.org/gems/i2c-devices

今のところあるのは

  • ACM1602NI
    • 秋月で売ってる I2C 16x02 液晶デバイス
  • AQM0802A
    • 秋月で売ってる I2C 8x2 液晶デバイス
  • HD44780
    • 液晶ドライバチップの HD44780 コンパチっぽいコマンドを I2C にインターフェイス変換した感じのものに使えるやつ
    • ACM1602NI, AQM0802A の親クラスとして実装してあるけど、実際は ACM1602NI 以外で動作確認はしていない
  • MPL115A2
    • 気圧計センサー
  • ADT7410
    • 高精度温度センサ (±0.5℃) データシートを元に実装しただけで、まだ動作確認してない
  1. トップ
  2. tech
  3. Ruby の I2C ライブラリ