エレキジャック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は、今回初めて使いました。なかなか複雑でデバッグがたいへんでした。