この記事は Shinosaka.rb Advent Calendar 2016 の 14日目の記事です。
はじめに
GR-CITRUSは、Ruby(正確にはmruby)でプログラムが書けるマイコンボードです。
私の場合は、先にArduinoを使ったことがあったので、理解が早い部分が多かった反面、GR-CITRUSとArduinoの違いで戸惑った部分もありました。
今年の10月に発売されたばかりでまだ情報量が少ないので、同じように戸惑っている人の参考になれば幸いです。
開発環境
Arduinoと違って、ガッツリしたIDEが用意されている訳ではありませんが、公式サイトで解説されているRubicなど、簡単に開発が始められるソフトがいくつか存在しています。
私の場合はsetgem
を使うのに慣れてしまったので、主にターミナルから操作しています。
プログラムの自動実行
Arduinoは電源を入れるとスケッチが自動実行されます。
対して、GR-CITRUSの場合は、標準では自動実行されずに簡易なOSが立ち上がります。
自動実行させるには、特定のファイルを設置した上でボードに少し細工が必要です。
詳しくはファームウェアのドキュメントを参照。
Ruby Firmware on GR-CITRUS.pdf
1. 自動実行させるプログラムを指定
どちらかの方法で指定できます。
-
wrbb.xml
ファイルに自動実行させたいファイルを記載する -
main.mrb
という名前で保存する- 正確には、
wrbb.xml
が存在しない場合のデフォルトがmain.mrb
になる
- 正確には、
2. 自動実行モードに切り替える
これも、以下のどちらかの方法で切り替えます。
- ボード上の
J10
にハンダを盛ってショートさせる - ボード上の
JP2
にジャンパを挿してショートさせる
私の場合は、抵抗の足を切った余りの線をU字に曲げてJP2
に差し込みました。
プルアップ
普段から電子回路を扱い慣れている人にとっては、当たり前のことかも知れません。
Arduinoの場合は内部的にプルアップしてくれるモードがあるのであまり意識しなくても問題ないのですが、GR-CITRUSでは物理的に何とかしてあげる必要があります。
プルアップ抵抗とは
その目的は主として、「そのラインに何も接続されていない状態になった時」に、そのラインの電位を「Hレベル」に固定することにある。
CPU等が、回路からの入力を判断する基準は電圧である。しかしもし、回路がどこにも接続されない状態の場合、その回路のレベルはHかLかはっきりしない、宙に浮いた状態となる。これでは、CPU等は入力がONなのかOFFなのか判断できない。
プルアップは、入力レベルがVCCレベル(Hレベル)であることを明確にするために行なわれ、この目的のためにプルアップ抵抗が取り付けられる。
プルアップ - 通信用語の基礎知識
C言語とかで、宣言した直後の変数に入っている値が不定なのと似てますね。
プルアップしていないとき
折角なので、プルアップしていないときに入力値がどう変化しているのかを調べてみました。
@usb = Serial.new(0, 115200)
100.times do
@usb.print(micros().to_s)
@usb.print(", ")
@usb.println(analogRead(16).to_s)
delay(100)
end
上記のコードを実行した結果をグラフにするとこんな感じ。
analogRead()
の出力は0から1023の範囲ですので、微妙な範囲を上下しているのが分かります。
接続例
I2C接続のセンサーを繋ぐときはこんな具合です。
データ(DI)とクロック(CK)の信号線と並列に、5V電源のラインに繋がれている抵抗(1kΩ)がプルアップ抵抗です。
I2Cのreadとwrite
GR-CITRUSのI2c
クラスは、ArduinoであればWire
クラスに相当するのですが、read
・write
メソッドの意味が違うので注意が必要です。
Arduinoとの違い
GR-CITRUS | Arduino | 意味 |
---|---|---|
read | - | 指定したアドレスからデータを読み込み |
write | - | 指定したアドレスにデータを書き込む |
lread | read | リクエストして返ってきたデータを受け取る |
lwrite | write | 送信バッファに数値を追加する |
通常、I2Cデバイスとの送受信には、若干煩雑な手続きが必要です。
GR-CITRUSのread
・write
メソッドは、その手続きをカプセル化して、直感的に扱えるようにしたものです。
Arduinoと同等のメソッドとしては、lread
・lwrite
メソッドが用意されています。(頭の「l」は、「ローレベル」のことだと思います。)
この2つは単独では使えず、begin
やend
と組み合わせて使う必要があります。
気を付けないといけない点
上記のように便利にまとめてくれていることから、Arduinoのスケッチを移植するときは、基本的にGR-CITRUSの方がコードの量は減るはずです。
ただ、read
・write
はとてもシンプルな実装なので、そのまま使えないこともあり得ます。
例えば、adafruit/Adafruit_LED_BackpackのAdafruit_LEDBackpack.cppにこんな感じの関数があります。
void Adafruit_LEDBackpack::writeDisplay(void) {
Wire.beginTransmission(i2c_addr);
Wire.write((uint8_t)0x00); // start at address $00
for (uint8_t i=0; i<8; i++) {
Wire.write(displaybuffer[i] & 0xFF);
Wire.write(displaybuffer[i] >> 8);
}
Wire.endTransmission();
}
こういうのをGR-CITRUSに移植するときは、lwrite
を使って低レベルな実装をする方が分かりやすいかもしれません。
まとめ
こんな具合に戸惑いポイントは色々ありましたが、何とか自分で解決できそうな範囲で楽しくやれています。
まだまだ書籍なんかは無い状況ですが、コードはともかく電子回路の作例はArduinoやIchigoJam向けのものがほぼそのまま使えますので、意外に困らなかったりします。
微妙な修正のために回路を頑張って理解したりするので、そういう意味で勉強になったかも。
何と言っても、Rubyで書けるのが快適で素晴らしい!
おまけ: I2Cスキャナ
I2Cデバイスの配線をしたあと、いきなりコードを書き始めるよりは、とりあえず配線がうまくいっているか確認したいものです。
こういった用途向けに、とりあえずデバイスとお話ができているかを確認するコードを書いてみました。(each
が使えるって素敵。)
コードと使い方
I2Cバスの全アドレスに読み込みを試してみて、成功したものだけ表示します。
I2CデバイスをGR-CITRUSと繋いでみたときに、とりあえずの動作確認で使えるかと思います。
#!mruby
@Usb = Serial.new(0, 115200)
@Dev = I2c.new(1) # SDA=>pin0, SCL=>pin1
@Address = 0x00 # Register address to read.
@Usb.println("I2C Scanner")
(1..127).each do |i|
r = @Dev.read(i, @Address)
@Usb.println(i.to_s(16) + ' ' + r.to_s(16)) if r != 255
end
この例だと、各I2Cデバイスのレジスタ0x00
を読み込んでデータが取得できれば表示する、というのをI2Cアドレス0x01
から0xEF
まで繰り返します。
加速度センサーのKP-ADXL343を、プルアップ抵抗の解説のように繋いで実行するとこんな感じ。
WAKAYAMA.RB Board Ver.CITRUS-2.19(2016/11/19)f3(256KB), mruby 1.2.0 (H [ENTER])
>R i2c
I2C Scanner
53 e5
出力があっさりすぎてアレですが、I2Cアドレス0x53
のデバイスが0xe5
を返しています。