Temperature Sensor(2)

Raspberry Piで正しく扱えないI2Cデバイスが存在する問題、ドライバのソフトウェア的な問題だと思っていたのだが、その後色々と調べてみるとBroadcomのBCM2835チップのI2Cにバグがあるという話が見つかった。Raspberry Piに使われているのが、まさにそのBCM2835だ。

I2C Broadcom bug workaround.
Fixing the Repeated Start Condition of the I2C Bus on the Raspberry Pi by Hardware

思ったよりも根が深い問題のようだ。

チップのバグならドライバではどうしようも無いが、Perl用のHiPiモジュールではsmbusでダメなI2Cデバイスでも正しく読み書きできているように思える。HiPiのI2Cツールの実行が妙に遅いことを考えると、こちらはGPIO直叩きで読み書きしているのかも知れない。

BCM2835チップのバグについて議論されているスレッドでは、カーネルにGPIO直叩きI2C(Bitbang I2Cと書かれていた)を実装して、そちらを使うという案が出されていたので、対処する方法が無いわけでは無いようだ。チップのバグなら新しいリビジョンで修正されるかも知れないが、既存の基板が交換されるとは思えないので何らかの対処は行われる事になるのだろう。

そうでないと困る。

今のところPerlのHiPiでなら使えることが判っているので、全くダメな状態よりはまだマシ。前回作った温度読み出しのテストプログラムにI2C液晶の表示を付け加えて、温度計にしてみた。R0012031

プログラムは以下の通り。もちろんHiPiがインストールされている必要がある。

#!/usr/bin/perl

# ADT7410 I2C Temperature Sensor & I2C LCD

use strict;
use warnings;
use HiPi::BCM2835::I2C qw( :all );

my $adt = HiPi::BCM2835::I2C->new(
            peripheral => BB_I2C_PERI_1,
            address    => 0x48
        );
my $lcd = HiPi::BCM2835::I2C->new(
            peripheral => BB_I2C_PERI_1,
            address    => 0x3e
        );
# init LCD
$lcd->i2c_write(0x00, 0x38, 0x39, 0x14, 0x7a, 0x56, 0x6c);
$lcd->delay(200);
$lcd->i2c_write(0x00, 0x38, 0x0c, 0x01, 0x06);
$lcd->delay(200);
$lcd->i2c_write(0x00, 0x38);
# main
my @val;
my $tmp;
my @param;
while(1)
{
    @val = $adt->i2c_read_register_rs(0x00, 2);
    #$val[0] = 0xff; # check value
    #$val[1] = 0xff;
    $tmp = ($val[0] << 8 | $val[1]) >> 3;
    if($tmp >= 4096)
    {
        $tmp = ($tmp - 8192);
    }
    my $out = sprintf("Temp.[%7.2f%cC]", $tmp / 16, 0xdf);
    $lcd->i2c_write(0x00, 0x38, 0x80);
    @param = (0x40, unpack("C*", $out));
    $lcd->i2c_write(@param);
    sleep(1);
}

HiPiが/dev/memにアクセスするため、実行にはsudo権限が必要。

温度センサから読み出したデータをそのまま液晶に表示しているだけで、AVRやPICのような8ビットマイコンでも同じ事ができてしまうからわざわざRaspberry Piを使う必要は無いのだが、Raspberry Piならデータをネットワークで送ったり、データベースに蓄積したりといった応用が比較的簡単にできるので、そういった用途であれば意味があるだろう。

Temperature Sensor

秋月電子通商温度センサーモジュールがRaspberry Piのsmbusモジュールでは正しく使えなかったので、PerlモジュールHiPiのI2Cツールで試すとこちらでは正しく値が読み出せる事が確認できた。

HiPiに含まれるI2Cモジュールのソースコードを読むと、デバイスのレジスタを読み出す関数に「i2c_read_register」と「i2c_read_register_rs」の二種類があり、後者がRepeated Start Conditionが必要なデバイスに対応した関数のようだ。そこまで判ればあとは実際に試してみれば良い。

まずは現在の温度を1秒毎に読み出して表示するだけの、単純なPerlプログラムを作って試してみた。

#!/usr/bin/perl

# ADT7410 I2C Temperature Sensor Test

use strict;
use warnings;
use HiPi::BCM2835::I2C qw( :all );

my $dev = HiPi::BCM2835::I2C->new(
            peripheral => BB_I2C_PERI_1,
            address    => 0x48
        );
my @id = $dev->i2c_read_register_rs(0x0b, 1);
print sprintf("device id -> %d.%d\n", ($id[0] >> 3), $id[0] & 0x03);
my @val;
my $tmp;
while(1)
{
    @val = $dev->i2c_read_register_rs(0x00, 2);
    #$val[0] = 0xff; # check value
    #$val[1] = 0xff;
    $tmp = ($val[0] << 8 | $val[1]) >> 3;
    if($tmp >= 4096)
    {
        $tmp = ($tmp - 8192);
    }
    print "current temperature -> ",$tmp / 16,"\n";
    sleep(1);
}

モジュールがデバイスに直接アクセスするため、実行するときはsudoを付ける必要がある。
実行結果は以下のようになった。保冷剤をセンサに近づけた状態で実行したので、温度が急速に下がっているのが判る。

adt7410

今までのI2Cデバイスと同じように、Pythonで簡単に計測できるものと思っていたのだが、意外な所で引っかかった。Perlモジュールが公開されていなかったら、もっと面倒な事になっていただろう。

Raspberry Piで色々と試すのはPythonが最も適していると思っているので、Python用のライブラリでも問題無く使えるようになって欲しい。