#freeze
[[FrontPage]]

2008/10/21 からのアクセス回数 &counter;

#contents

I2Cマスターが正常に動作したので、今度はI2Cスレーブの動作確認をします。
マスターのところで、つまずいてHCS08マイコンでIC2マスターを組み立てたので
これを利用してI2C通信のテストをすることにしました。(災い転じて福となす)

** 回路の説明 [#h351ecd4]
ATmega88とHCS08の接続は、以下の通りです。

#ref(0.jpg);

** スレーブのプログラム [#vf1022a5]
スレーブの処理は、TWIによる割り込み処理で行われ、AVRlibでは割り込み時に呼ばれる
コールバックルーチンを登録します。

- i2cSetSlaveReceiveHandlerでスレーブに送られたデータを読み込むコールバックを登録
- i2cSetSlaveTransmitHandlerでマスターに送るデータをセットするコールバックを登録

します。

*** 割り込み処理の間はUARTのデバッグプリントが使えない [#ka7bb08d]
当然のことですが、やはり間違えてしました。割り込み処理は、できるだけ短時間で割り込み
元にリターンする必要があるので、UARTを使ったデバッグプリントが使えません。

そこで、グローバル変数cmdにデバッグプリントの命令をセットし、mainの無限ループで
データのチェックプリントを出力することにしました。

*** プログラムソース [#p2901c2b]
I2Cスレーブの処理は、

+ I2Cの初期化
+ スレーブのアドレスをセット
+ I2C通信の受信と送信時のコールバック関数を登録

だけです。

- 受信コールバック関数では、受信データをローカルバッファにコピー
- 送信コールバック関数では、LM73の温度データと同じ2バイトのデータを送信

することにします。

プログラムソースは以下の通りです。
#pre{{
#include "global.h"		// include our global settings
#include "uart.h"		// include uart function library
#include "rprintf.h"	// include printf function library
#include "i2c.h"		// include i2c support

#define LOCAL_ADDR	(0x05<<1)
#define	CB_W	(0)	// send from master
#define CB_R	(1) // read by master

char	cmd=0;
u08	localBuffer[20];
u08	localBufferLength;

// slave operations
void i2cSlaveReceiveService(u08 receiveDataLength, u08* receiveData)
{
	int i;
	
	cmd = 'r';	
	
	// copy the received data to a local buffer
	for(i=0; i<receiveDataLength; i++)
	{
		localBuffer[i] = *receiveData++;
	}
	localBufferLength = receiveDataLength;
}

u08 i2cSlaveTransmitService(u08 transmitDataLengthMax, u08* transmitData)
{
	cmd = 't';
	
	transmitData[0] = 0x0D;
	transmitData[1] = 0xE0;
	localBufferLength = 2;
	
	return localBufferLength;
}

int main(void)
{	
	int i;
	int c;
	
	uartInit();
	uartSetBaudRate(9600);
	rprintfInit(uartSendByte);
	rprintf("Start I2C Salve\n");
	
	i2cInit();
	i2cSetLocalDeviceAddr(LOCAL_ADDR, FALSE);
	i2cSetSlaveReceiveHandler( i2cSlaveReceiveService );
	i2cSetSlaveTransmitHandler( i2cSlaveTransmitService );
	
	rprintf("wait master call\n");
	
	while(1) {
		if (cmd == 'r') {
			rprintf("recieve data: ");
			for(i=0; i<localBufferLength; i++)
			{
				rprintf("%x", localBuffer[i]);
			}
			rprintf("\n");
			cmd = 0;
		}
		else if (cmd == 't') {
			rprintf("transmit dummy data\n");
			cmd = 0;
		}
	}

	return 0;
}
}}

** 操作確認 [#v8c4756a]
実際に接続して動かしてみると

#pre{{
$jerm -b 9600 -p none -s 1 -d 8 /dev/u.usbserial-A5002yHm 
途中省略
Start I2C Salve
wait master call
recieve data: 000400E0
recieve data: 0000
transmit dummy data
}}

と出力される。rprintfの%xは、2バイト分を出力するので、実際には04E0を受信しています。



** コメント  [#c5865721]
この記事は、

#vote(おもしろかった[22],そうでもない[0],わかりずらい[19])
#vote(おもしろかった[23],そうでもない[0],わかりずらい[19])

皆様のご意見、ご希望をお待ちしております。
- 分かりづらいとのご指摘を受けたので、変更しますので、しばらくお待ち下さい。 -- [[竹本 浩]] &new{2008-10-23 (木) 14:55:39};
- I2C 通信は、(start)+Slave address + offset address + data ...(stop) という並びが Write で、(start) - Slave + offset +(re-start) + (slave+1) + data ... (stop) が読み出しです。 このサンプルコードはどちらにも合わないと思いますが、対処するにはI2C write の data の最初の 1byte を常にメモリすることでよいのでしょうか? -- [[kanmei]] &new{2009-11-14 (土) 20:27:51};
- ご指摘の部分は、別の記事「I2Cを使う(マスター編)」のオシロスコープのところで確認しています。この例は、I2Cを割り込みによって処理する時のスレーブの例題で、i2cSetSlaveReceiveHandler( i2cSlaveReceiveService );、i2cSetSlaveTransmitHandler( i2cSlaveTransmitService );で割り込み処理関数をセットしている部分がポイントです。自分のI2Cアドレスはi2cSetLocalDeviceAddr(LOCAL_ADDR, FALSE);でセットしています。ご指摘のとおり、どのように通信が行われているか分かりずらいので時間ができたら、もう少し整理して説明します。 -- [[竹本 浩]] &new{2009-11-14 (土) 21:21:12};
- コメントありがとうございます。マスター編のオシロスコープの画面を見ても Start + (Slave+readbit 1) + data + stop というシーケンスになっています。このオシロスコープの画面の前に start - slave - offset というシーケンスがあり、画面で示されているのは start ではなく re-start ということでしょうか? AVR の i2c.c を見ても対応していないように見えます。switch 文の TW_SR_DATA_ACK のところで offset address をメモリしておかなければならないと思います。今回のサンプルコードでも常に先頭バイトからの出力のみしか対応できないのではないかと思いますが、如何でしょうか? -- [[kanmei]] &new{2009-11-15 (日) 20:17:02};

#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc