フォト
2017年5月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ

« 書籍「挿すだけ!ARM32ビット・マイコンのはじめ方」を購入 | トップページ | IchigoJamとArduinoでI2Cバス通信を試してみる (2) »

2015年5月 1日 (金)

IchigoJamとArduinoでI2Cバス通信を試してみる

IchigoJamとArduino間でI2Cバスを使ってデータ通信を試してみました。
Arduinoをスレーブとして接続し、マスタであるIchigoJamからの要求に答えます。

04

             Dscn3780

先に結論をいうと、ArduinoのI2Cライブラリ Wireにバグがあるため、
Arduinoをスレーブとして完全に使い込むことは出来ません。
ライブラリの修正が必要となります。

 
   => 2016/05/36 追記

        バグは1.6.6で対応されているようです。
        よって、本記事の内容はArduino IDE 1.6.5までのお話しです。
        また、ArduinoのSDA/SCL端子はプルアップ抵抗有効化されているため
        回路図の2.2kΩ抵抗は無くても動作します。

まずは、正常に動作するIchigoJamからArduinoにデータを送信するパターンの
動作確認です。

1)IchigoJamからコマンド2バイト+データ4バイトをArduinoに送信する例

IchigoJam側のプログラム(I2Cマスタ)
    10 'send data
    20 POKE #700,1,2
    30 POKE #702,3,4,5,6
    40 R=I2CW(#10,#700,2,#702,4)
    50 ?R
    60 END

#700番地から格納されている2バイトデータ 1, 2 をコマンド、
#702番地から格納されている4バイトデータ 3,4,5,6 をデータとして
I2Cアドレス #10(7ビット)に送信します。

Arduinoのプログラム   

#include <Wire.h>

// マスターからを受信
void receiveEvent(int n) {
  byte d;
  Serial.println("recept data");
  for (int i=0; i < n; i++) {
    if(Wire.available()) {
        d = Wire.read();
        Serial.print(d,HEX);
        Serial.print(" ");
    }
    Serial.println();
  }
}

//セットアップ
void setup() {
  Serial.begin(115200);
  Wire.begin(0x10) ;                 // I2Cの初期化、自アドレスを10とする
  Wire.onReceive(receiveEvent) ;     // マスタからのデータ送信対応のコールバック関数登録
  Serial.println("i2c slave test");
}

void loop() {
}

I2Cアドレスとして#10を設定し、データ受信時に呼び出されるコールバック関数
recevEventを登録し、その関数内でデータを表示しています。

実行結果

IchigoJam側

03

正常終了しました。

arduino側も受信したデータを問題なく表示しました。

02

次に問題のIchigoJamからのデータ取得要求について試してみます。
IchigoJamでArduinoから送られてくるデータを受信するパターンです。

2)IchigoJamでArduinoからのデータを受信する

具体的な例としてはEEPROMからデータを取得するような通信です。

05

一回の通信で、IchigoJamからI2Cアドレスとデータを送信し、Arduino側からの
送信データを受信するパターンです。

IchigoJam側のプログラム

 10 'request data
 20 POKE #700,0,0
 30 R=I2CR(#10,#700,2,#702,4)
 40 ?R
 50 FOR I=0 TO 3
 60 ?HEX$(PEEK(#702+I),2)
 70 NEXT
 80 END

#700番地から格納されている2バイトデータ 1, 2 をコマンド、
#702番地から4バイト分がスレーブから受信データを格納する領域として指定、
I2Cアドレス #10(7ビット)にデータをリクエストします。

Arduino側のプログラム

#include <Wire.h>

// マスターからを受信
void receiveEvent(int n) {
  byte d;
  Serial.println("recept data");
  for (int i=0; i < n; i++) {
    if(Wire.available()) {
        d = Wire.read();
        Serial.print(d,HEX);
        Serial.print(" ");
    }
    Serial.println();
  }
}

// マスターからのリクエストに対するデータ送信
void requestEvent() {
  Serial.println("resept request.");
  Wire.write("Hello");
}

//セットアップ
void setup() {
  Serial.begin(115200);
  Wire.begin(0x10) ;                 // I2Cの初期化、自アドレスを10とする
  Wire.onRequest(requestEvent) ;     // マスタからのデータ取得要求のコールバック関数登録
  Wire.onReceive(receiveEvent) ;     // マスタからのデータ送信対応のコールバック関数登録
  Serial.println("i2c slave test");
}

void loop() {
}

データ取得要求に対して、requestEvent()で応答を返します。 これを実行すると
IchigoJamにデータが返されず、通信もエラーとなります(Rの値が0でない)。

07

Arduino側のターミナル出力にはrequestEvent()で記述しているメッセージが
表示されません。
ロジックアナライザで調べてみると、

01

2バイトのデータ送信後、受信に切り替えるリピートStart+I2Cアドレス送信が
うまくいってないようです。
当初、原因が分からなかったのですが、Arduino側が明らかにおかしいと判断し、
I2Cライブラリ Wireを調べると、原因がわかりました。

\libraries\Wire\utility\twi.cのISR(TWI_vect)のリピートStartの処理で次のようなことを
行ています。

    case TW_SR_STOP: // stop or repeated start condition received
      // put a null char after data if there's room
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        twi_rxBuffer[twi_rxBufferIndex] = '\0';
      }
      // sends ack and stops interface for clock stretching
      twi_stop();
      // callback to user defined callback
      twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
      // since we submit rx buffer to "wire" library, we can reset it
      twi_rxBufferIndex = 0;
      // ack future responses and leave slave receiver state
      twi_releaseBus();
      break;

TW_SR_STOPはSTOPまたは、リピートSTARTを受信時に処理を行う箇所です。
この中で、リピートSTART受信のため、直前に受信したバッファ内データを
twi_onSlaveReceive()を実行してrecevEvent()を呼び出しています。
これにより、IchigoJamからの送信データは受信できます。

その後の処理が問題でこのタイミングでtwi_stop()を実行しています。
明らかにおかしいと思い、色々と調べると、同様のことを指摘している情報が
いくつかありました。次のリンクはその一つです。

Wire Library doesn't handle Repeated START commands in I2C slave mode [imported] #848

上記の文書では、twi_stop()をコメントアウトすることで対応可能と書かれています。

実際にやってみると、

08

正常に動作しました。

Arduino側のモニター出力にも正常に呼び出されたメッセージが表示されました。

ということで、ArduinoのI2Cライブラリのスレーブ処理にはバグがあります。
最新版の1.6.4でも試したのですが、いまだに対処されていないようです。

なぜ、対応されないのでしょうね。まあ、スレーブとして使うことはあまりないですからね。

2016/04/30 追記
上記のバグは1.6.6で対応されているようです。

リリースノートの抜粋

01

下記は1.6.8(現時点最新版)
  \Arduino 1.6.8\hardware\arduino\avr\libraries\Wire\src\utility\twi.cのISR(TWI_vect)
 
    case TW_SR_STOP: // stop or repeated start condition received
      // ack future responses and leave slave receiver state
      twi_releaseBus();
      // put a null char after data if there's room
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        twi_rxBuffer[twi_rxBufferIndex] = '\0';
      }
      // callback to user defined callback
      twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
      // since we submit rx buffer to "wire" library, we can reset it
      twi_rxBufferIndex = 0;
      break;

« 書籍「挿すだけ!ARM32ビット・マイコンのはじめ方」を購入 | トップページ | IchigoJamとArduinoでI2Cバス通信を試してみる (2) »

arduino」カテゴリの記事

IchigoJam」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/571408/61523524

この記事へのトラックバック一覧です: IchigoJamとArduinoでI2Cバス通信を試してみる:

« 書籍「挿すだけ!ARM32ビット・マイコンのはじめ方」を購入 | トップページ | IchigoJamとArduinoでI2Cバス通信を試してみる (2) »