そのボード、僕が積んでおきます。

たくさんの電子工作基板が積みっぱなしになってるので、頑張って工作しようとしていますが、どんどん溜まっていっております。

Espruino入門【I2C編】 Espruino Advent Calendar 2014

ちょっと(かなり)前に、Espruinoに関する2つのエントリーを書きました。

 ・Espruino環境を整える - そのボード、僕が積んでおきます。

 ・Espruino入門【Lチカ編】 - そのボード、僕が積んでおきます。

 

しばらく他のことに現を抜かしていたのですが、QiitaのAdventCalendar一覧を見ていたら、Espruinoのカレンダーがあったので急遽Espruinoの続きにチャレンジしました。このエントリーはEspruino Advent Calendar 2014の12月10日分のエントリーです。

 

今回は前回最後の予告通りI2Cの通信にチャレンジしてみたいと思います。

 

最近はI2Cで通信するセンサー類も増えていますし、簡単な表示器もI2C対応していますのでI2Cのリードライトができれば、結構色々なパターンの工作ができます。まさにIoTの基本はI2Cにあり、ということで、I2Cです。

 

【作ったもの】

Espruinoでコントロールする大気圧計

f:id:hine:20141211010324j:plain

 

今回は、比較的メジャーな2つのデバイスをコントロールします。

・センサー:大気圧センサー「LPS331」

・表示器:小型キャラクターLCDAQM0802A

どちらも、秋月で手に入る、と思ったら、大気圧センサーがもう秋月にはないみたいです。なので、参考程度に。今回はハードウェア側は手を抜きたかったので、以前に作っていたトランジスタ技術2014年2月号の付録のI2C学習基板を用いました。

 

そして、今回のメイン基板は前々回にEspruino化したSTM32F4 Discoveryです。Advent Calendarでは純正基板を使ってる方が多くて羨ましいですが、とりあえず、僕は手持ちのボードで行きます。

 

さて、Espruinoは標準環境でも様々なデバイスに対応しています。

Espruino - Modules

ところが運悪く、今回使うデバイスは含まれていませんでしたので、I2Cライブラリを使い個別に処理を記述することにします。

 

基本的にI2C通信はそんなに難しい処理ではありません。デバイスのI2Cアドレスがわかり、制御方法がわかれば移植は容易ですので、Arduinoでの実装例をベースに作っていきます。

 

delay()関数

 

デバイスのコントロールを書く前に、I2Cの制御ではタイミングが重要となることもあるので、こちらのページを参考にして、Arduinoでお馴染みのdelay()関数を実装します。

function delay(msec) {
    var time0 = getTime();
    var time1 = 0;
    while(true) {
        time1 = getTime();
        if ((time1 - time0) * 1000 >= msec) {
            break;
        }
    }
}

 

LCDのコントロール

 

僕は何か表示されるとテンションが上がるタイプなので、まずはLCDのコントロールから実装しようと思います。

 

参考にしたのはこちらのページです。

 

まずは、I2Cの初期化を行わなければなりません。今回はI2C1を利用します。ピンの位置はこのページを参考にしています。

Espruino - ReferenceSTM32F4DISCOVERY

 

I2C接続は、電源、GND、I2Cクロック(SCL)、I2Cデータ(SDA)の4本をつなぐだけで良く、非常にシンプルな接続が可能です。複数の機器を繋ぐ場合も、全ておなじ系統につなぐだけでOKです。SCLとSDAはバスのどこかにプルアップ抵抗を挟む必要がありますが、今回はテスト基板でプルアップ抵抗が実装済みですので単純に4本の線を接続しただけです。

 

そして、EspruinoでのI2Cの初期化は以下のように行います。

I2C1.setup({scl:B6,sda:B7});

これで、B6ピン、B7ピンはI2C接続で利用できるようになります。

 

あとは、Androidのコードを粛々と移植していきます。

function lcd_cmd(x) {
    I2C1.writeTo(0x3e, 0x00, x);
}

 

function lcd_data(x) {
    I2C1.writeTo(0x3e, 0x40, x);
}

 

function lcd_init() {
    delay(500);
    lcd_cmd(0x38);
    lcd_cmd(0x39);
    lcd_cmd(0x14);
    lcd_cmd(0x70);
    lcd_cmd(0x56);
    lcd_cmd(0x6c);
    delay(200);
    lcd_cmd(0x38);
    lcd_cmd(0x0c);
    lcd_cmd(0x01);
    delay(2);
}

 

function lcd_puts(str) {
    for (var i = 0;i < str.length; i++) {
        lcd_data(str.charCodeAt(i));
    }
}

 

function lcd_move(pos) {
    lcd_cmd(0x80 | pos);
}

 

function lcd_clear() {
    lcd_move(0x00);
    lcd_puts("        ");
    lcd_move(0x40);
    lcd_puts("        ");
}

 今回テスト程度のコードですので、ハードコーディングです。lcd_cmd()とlcd_data()の中でI2C1.writeTo()の第一引数として登場している0x3eがこのデバイスのI2Cアドレスです。あとは、見ての通り、初期化手順に沿って初期化することと、lcd_move()でデータ書き込み位置を指定し、lcd_data()でデータを送り込みます。

画面のクリアはシンプルに「スペースで埋める」という手法で行います。この中で出てくる0x40は、2行目の一番左のアドレスになります。

 

I2Cの初期化後であれば、

lcd_init();

lcd_move(0x00);

lcd_data("TEST");

で画面にTESTという文字が表示されるはずです。

f:id:hine:20141211025141j:plain

おお、これはテンション上がる。この勢いでセンサーの方も行けそうです。

  

大気圧センサーからのデータ取得

 

大気圧センサーの制御はこちらのページを参考にしました。



こちらはデータの取得があります。writeでデータの書き込み、readがデータの読み取りです。

function pressure_write(address,data) {
    I2C1.writeTo(0x5c, address, data);
}

 

function pressure_read(address) {
    I2C1.writeTo(0x5c, address);
    return I2C1.readFrom(0x5c, 1);
}

 

function pressure_init() {
    pressure_write(0x20, pressure_read(0x20) & 0x7f);
    pressure_write(0x10, 0x7a);
    pressure_write(0x20, 0x04);
    pressure_write(0x20, 0x84);
    delay(2);
}

例によって、pressure_write()とpressure_read()の中に登場する0x5cはこのデバイスのI2Cアドレスです。

 

データの取得に関しては、参考にさせていただいたページよりも簡易化して以下のようにしました。

function pressure_get() {
    pressure_write(0x21, pressure_read(0x21) | 0x01);
    while ((pressure_read(0x27) & 0x03) != 0x03) {
    }
    var press = pressure_read(0x2a);
    press = pressure_read(0x29) | (press << 8);
    press = pressure_read(0x28) | (press << 8);
    return (press / 4096);
}

この関数で、全16桁の小数でヘクトパスカル値が返ってきます。

 

関数をまとめあげる

 

この結果を8桁できり捨てて1行目に表示する関数を作成します。この関数をsetInterval()で指定する想定です。

function refresh_display() {
    lcd_move(0);
    lcd_puts(pressure_get().toString().substr(0, 8));
}

 こういう関数を作成しました。

 

最後にこれらをEspruinoの流儀で呼び出すようにします。

function onInit() {
    I2C1.setup({scl:B6,sda:B7});
    lcd_init();
    pressure_init();
    lcd_clear();
    lcd_move(0x45);
    lcd_puts("hPa");
    var interval = setInterval(refresh_display, 500);
}

 電源投入後に、I2Cの初期化、LCDの初期化、大気圧センサーの初期化、画面に「hPa」の表示をした後に、500msecごとに画面書き換えをするようにしています。

 

これで大気圧計の出来上がりです。即席のコードなので、JavaScriptっぽさの全くないベタ書きのプログラムですが。

 

前にも書きましたが、シリアルで接続したボードで直接プログラムを実行しながら書いていけるので、プロトタイピングにはもってこいです。よく使っている他の環境のような、ビルドして書き込み、という手順に比べて圧倒的な手軽さです。これこそがEspruinoの最大の魅力だと感じます。

 

次回はもう少しJavaScriptっぽい記述になるように書き換えてみたいと思います。