Sim's blog

電子工作はじめてみました

温度センサーLM73を使ってみる

2008-09-23 18:54:02 | AVR
エレキジャックNo.8に、ナショナルセミコンダクタの温度センサーLM73が載った基板が付録についてきました。マイコンとはI2Cで通信します。

LM73基板です。

足とパスコン(0.1uF)をつけています。パスコンはつけなくても、基板の外(2pinと3pinの間)につけてもいいです。穴が開いているのでチップコンデンサでなくてもいいのですが、チップ部品のはんだづけの練習もかねてつけてみました。ちょっとななめってます。

pin配置です。エレキジャックにpin配置が載っていなかったので、データシートから写しました。
1 ADDR I2Cのslave addressを決めるpin。openで0b1001100になる
2 GND
3 Vdd 2.7~5.5V
4 SMBCLK I2CのSCL (要pull up)
5 #ALERT 範囲外出力
6 SMBDAT I2CのSDA (要pull up)

つなぐ必要があるのはGND(2pin)、Vdd(3pin)、SMBCLK(4pin)、SMBDAT(6pin)の4本です。内部レジスタに設定した範囲を超えるとALERT信号を出すことができますが、使わなければつながなくていいです。SMBCLKとSMBDATのpull up抵抗はエレキジャックのミッション2第1章では3.3kΩ、第2章では1kΩです。ちなみに第1章ではLM73をPICに、第2章ではHCS08につないでいます。

このところAVRづいているので、AVRにつないでみます。

I2C通信の参考にしたサイトです。
趣味の電子工作の部屋 by すん I2Cコントロール実験
AVR試用記 I2C通信
エレキジャック 1、2、3線シリアル・インターフェース

I2Cでマスターからスレーブにデータを送るには以下の手順になります。
(1) スタートビットを送る
(2) スレーブのアドレスとWを送る(SLA+Wといいます)。
(3) ACK/NAKをもらう
(4) データを送る
(5) ACK/NAKをもらう。必要なだけ(4)と(5)を繰り返す
(6) ストップビットを送る

I2Cでマスターからスレーブのデータを受け取るには以下の手順になります。
(1) スタートビットを送る
(2) スレーブのアドレスとRを送る(SLA+Rといいます)。
(3) ACK/NAKをもらう
(4) データを受信する
(5) ACKを返して、さらにデータをもらうために(4)に戻るか
NAKを返して、もうデータがいらないことを伝える
(6) ストップビットを送る

上の2つを組み合わせたパターンもあります。そのときは、受信時のスタートビットはリピーテッド・スタートと名前が変わります。また、上の手順ではエラーチェックをしていませんが、やたら細々とエラーチェックが必要になります。

ATMega88はTWIというI2Cのハードがついているので、レジスタをいじることでI2C通信ができます。全部ソフトで書くのよりは随分と楽になります。

実験している様子です。

上に見えているのがライター代わりの78K0付録基板(トラ技8月号)です。左側がRS232C通信用の秋月製FT232RL USBシリアル変換モジュールです。中央がAVR ATMega88で、右側が今回のLM73付録基板です。秋月で売っている小型温度計モジュール(通販番号M-01063)で温度を測っています。温度計の影になっていますが電源は3.3VのACアダプタです(実測3.5V)。
0.5秒毎に、LM73で測った温度データはI2CでATMega88に読み込まれてUSBシリアル変換モジュール経由でPCに送られます。

ソースです。<と>は全角になっています。
#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

typedef unsigned char byte;

// USART関連は省略

// 16進1桁出力
void puthex1(byte d)
{
    d += '0';
    if(d > '9') d += 'a' - '9' - 1;
    putch(d);
}

// 16進2桁出力
void puthex2(byte d)
{
    puthex1(d >> 4);
    puthex1(d & 0x0f);
}

// 10進2桁出力 (x < 100のみ可)
void putdec2(byte x)
{
    putch('0' + x / 10);
    putch('0' + x % 10);
}

// エラー
void i2c_error(void)
{
    putch('['); puthex2(TWSR); putch(']'); // TWSRの内容
    putch(0x0d); putch(0x0a); // 改行
    PORTB = 0x01; // LED点灯
    while(1) ;
}

// 通信速度の初期化
void i2c_init(void)
{
    TWSR = 0b00000000;  // 1分周
    TWBR = 32;          // 100k = 8MHz / (16 + 2 * TWBR * 1)
}

// master 1byte送信
void i2c_write(byte d)
{
    TWDR = d; // 送信データ
    TWCR = _BV(TWINT) | _BV(TWEN);
    while( !(TWCR & _BV(TWINT)) ) ; // データの送出完了待機
    if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK) i2c_error();
}

// master 1byte受信(ackを返す)
byte i2c_read_ack(void)
{
    TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
    while( !(TWCR & _BV(TWINT)) ) ; // 受信完了待ち
    if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK) i2c_error();
    return TWDR; // データを返す
}

// master 1byte受信(noackを返す)
byte i2c_read_nak(void)
{
    TWCR = _BV(TWINT) | _BV(TWEN);
    while( !(TWCR & _BV(TWINT)) ) ; // 受信完了待ち
    if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK) i2c_error();
    return TWDR; // データを返す
}

// master送信開始、IDはslave address << 1
void i2c_start(byte id)
{
    byte s;

    // 開始条件を送る
    TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
    while( !(TWCR & _BV(TWINT)) ) ; // 開始条件の送出完了待機
    s = TWSR & TW_STATUS_MASK;
    if(s != TW_START && s != TW_REP_START) i2c_error();

    // アドレスを送る
    TWDR = id;
    TWCR = _BV(TWINT) | _BV(TWEN);
    while( !(TWCR & _BV(TWINT)) ) ; // アドレスの送出完了待機
    s = TWSR & TW_STATUS_MASK;
    if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

// master送信終了
void i2c_stop(void)
{
    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
    while( !(TWCR & _BV(TWSTO)) ) ;
    // statusはTW_NO_INFOになる
}


#define LM73 (0x4c << 1)    // LM73のslave address

int main()
{
    byte th, tl;

    DDRB  = 0x01;
    PORTB = 0x00;

    usart_init();
    i2c_init();

    // LM73設定
    i2c_start(LM73 | TW_WRITE);
    i2c_write(0x04); // register 4
    i2c_write(0x60); // 14bit resolution
    i2c_stop();

    // ポインタを0にしておく(readするだけで温度が読めるようになる)
    i2c_start(LM73 | TW_WRITE);
    i2c_write(0x00); // register 0
    i2c_stop();

    while(1){
        // 温度受信
        i2c_start(LM73 | TW_READ);
        th = i2c_read_ack(); // 上位読み込み
        tl = i2c_read_nak(); // 下位読み込み(最後はNAKを返す)
        i2c_stop();

        // 結果出力 (マイナスは考えない)
        puthex2(th);
        puthex2(tl);
        putch(' ');
        putdec2(th << 1 | tl >> 7); // 結果出力 整数部
        putch('.');
        putdec2(((tl & 0x7f) * 200) >> 8); // 結果出力 小数部
        putch(0x0d);    // 改行
        putch(0x0a);

        _delay_ms(500); // 0.5秒待つ
    }
}

USART関連(usart_init、getch、putch)は前回と同じなので省略しました。
i2c関係は以下です。
i2c_init(初期化)、
i2c_start(スタートビットを送る)、
i2c_stop(ストップビットを送る)、
i2c_write(1バイト、マスター→スレーブ通信)、
i2c_read_ack(マスター←スレーブ通信とack返答)、
i2c_read_nak(マスター←スレーブ通信とack返答)
エラーを見つけたらi2c_errorに飛んで無限ループになります。
i2c_startは厳密には4種類考えられます(R/Wとstart, repeat start)が、たいへんなので1つにまとめています。

TeraTermで受信している様子です。

左側が受け取った生データで右側が換算した値です。27℃くらいです。
受信データは、16ビット中最上位が符号ビット、8ビットが整数部、7ビットが小数部になっています。11ビット精度のときは、符号1 + 整数部8 + 小数部2です。14bitだと小数部が5ビットに伸びます。

I2Cは、今回初めて使いました。なかなか複雑でデバッグがたいへんでした。

最新の画像もっと見る

2 コメント

コメント日が  古い順  |   新しい順
Unknown (せくすぃ部長)
2008-09-24 09:15:08
これだけ細かい数字で温度を出力されると、もう合っているんだかどうだかさっぱり判らないですよね。

たまにサーミスタやLM35DZとか使って温度センサーを作りますが、そこいらじゅうの温度計を持って来ると、どれ一つとして同じ値を示さない(笑)。

http://hamayan.blog.so-net.ne.jp/2008-01-14
返信する
re:Unknown (Sim)
2008-09-24 22:35:14
こんにちは、せくすぃ部長さん

確かに何を測っているのか分からなくなってきます。測定器の一番大変な所は校正ですね。氷とか沸騰水といかホットプレート(!)なんて話もあるみたいですが、チップ形状になっていると、そういうわけにもいかないです。秋月の温度計より、ちょっと高めの数字が出ています。
とりあえずI2Cができるようになったので、何か他のアイテムも試してみようと思っています。その前にSPIかも。

返信する

コメントを投稿