#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