i2C(Wire)通信で教えてください。

GR-SAKURAです。

スレーブの受信で、受信ハンドラーのEventReceive関数を呼ばれるのですが、loop関数に戻ってこない。

マスターから1文字送ると受信文字数available()で0を読みます。この時loop関数から割り込みでWireの割り込みになっていると思います。

もう一度1文字送るとavailable()は1をだして、送った文字がreadで読めます。

この事からEventReceive関数は受信のたびに呼ばれていることはわかります。

しかしloop関数に戻ってこない。

間違いが分かる方いたら教えてください。

以下ソースです。

/* GR-SAKURA Sketch Template V2.24 */
#include <Arduino.h>
#include <wire.h>

void requestEvent( void )
{

    Wire8.write( "Hello ") ;
    Serial.println("I2c Request");

}

char I2cRcvData[32] ;
char iat;                                      // I2C 1 Byte Read
int bufc ;

void EventReceive( int numbyte )
{
    digitalWrite(PIN_LED0,1);                    // LED1 ON

    bufc = 0 ;
    while( Wire8.available() ) {
           digitalWrite(PIN_LED1,1);                    // LED OFF!

        bufc++ ;
        iat = Wire8.read() ;                                 //  1バイトの読込み        bufc++ ;
        I2cRcvData[bufc] = iat ;
        if( iat == 0x0a ) {
            I2cRcvData[0] = bufc ;                         // 受信受信文字数
            I2cRcvData[bufc+1] = '\0' ;
        }
        Serial.print( iat );                                   // 受信文字
    }
}

void setup()
{

    Serial.begin(57600);

    pinMode(PIN_LED0,OUTPUT);
    pinMode(PIN_LED1,OUTPUT);
    pinMode(PIN_LED2,OUTPUT);
    pinMode(PIN_LED3,OUTPUT);

}

void loop()
{

    Serial.println("I2c INIT");

    Wire8.begin(0x40) ;
    Wire8.onRequest(requestEvent);              // 送信要求がMasterから来たら呼ばれる
    Wire8.onReceive(EventReceive);              // Masuterからデータ受信したら呼ばれる

    while( 1 ) {
        digitalWrite(PIN_LED2,1);                    // LED2 ON Ether ON
        delay(500) ;
        digitalWrite(PIN_LED2,0);                    // LED2 ON Ether OFF!
        delay(500) ;
    }
}

Parents
  • Arduino Wireライブラリを使ったことはないですが、Arduinoのloopは基本的にちゃんと定期的に戻りが必要だったはずです(がじぇっとルネサスもかは確証なし)。まずはwhile(1)というのはやめましょう。loopの呼び出し元がloopの前後で色々やってるはず。

    Wireの使い方は以下をご参考に

    https://deepbluembedded.com/arduino-i2c-slave/

    なぜloop()で止まってダメなのかは実際のloop呼び出しコードをみてみればわかりますが、古いコードの時の場合、loop呼び出し戻ってきた時にシリアルバッファ処理が実行されてます。while(1)でブロックしてしまうと非同期な処理が止まってしまいます。

    https://forum.arduino.cc/t/void-loop/297970/8

  • 回答ありがとうございます。

    loop()に戻すようにしてみましたが結果は同じでした。
    マスターから1Byte 送るとスレーブのLEDの点滅が止まってしまう同じ結果でした。
    void setup()
    {

        Serial.begin(57600);

        pinMode(PIN_LED0,OUTPUT);
        pinMode(PIN_LED1,OUTPUT);
        pinMode(PIN_LED2,OUTPUT);
        pinMode(PIN_LED3,OUTPUT);

        Wire8.begin(0x40) ;
        Wire8.onRequest(requestEvent);              // 送信要求がMasterから来たら呼ばれる
        Wire8.onReceive(EventReceive);              // Masuterからデータ受信したら呼ばれる

    }

    void loop()
    {

        digitalWrite(PIN_LED2,1);                    // LED2 ON Ether ON 接続!
        delay(500) ;
        digitalWrite(PIN_LED2,0);                    // LED2 ON Ether ON 接続!
        delay(500) ;
     
    }

  • EventReceiveはwhileから抜けてますか?

    Wire8.read()はデータがない場合、ブロックする仕様だったはず

    void EventReceive( int numbyte )
    {
        digitalWrite(PIN_LED0,1);                    // LED1 ON

        bufc = 0 ;
        while( numbyte ) {
               digitalWrite(PIN_LED1,1);                    // LED OFF!

            bufc++ ;
            iat = Wire8.read() ;                                 //  1バイトの読込み        bufc++ ;
            I2cRcvData[bufc] = iat ;
            if( iat == 0x0a ) {
                I2cRcvData[0] = bufc ;                         // 受信受信文字数
                I2cRcvData[bufc+1] = '\0' ;
            }
            Serial.print( iat );                                   // 受信文字

            numbyte --;
        }
    }

    こうだとどうですか?これで解決ならWire8.available() の実装がおかしい可能性もありますね。

  • read()ですが確認したらStreamの派生だったので戻り値はintでデータない時は-1を返す仕様でした。ブロックはないです。Wire8.read()の戻り値でwhile終了判定をしてもいいかもしれませんね。

    github.com/.../Wire.h

  • 回答ありがとうございます。

    numbyteをfor文にして実行しましたが、結果は同じでした。

    もっと簡単に以下にして1文字を送信したところ、1回目にはLED0が点灯しない。

    EventReceiveが呼ばれていない状態で、loop()の点滅も止まっている状態です。

    2文字目を送ると、EventReceiveが呼ばれて、LED1,LED2が点灯します。

    loop()の点滅も止まっている状態です。

    このことから、1文字目送信のどこかのハードステータスを読んでいる所でループしている?

    void EventReceive( int numbyte )
    {
        digitalWrite(PIN_LED0,1);           // LED ON
        iat = Wire8.read() ;                      // 1バイトの読込み
        digitalWrite(PIN_LED1,1);           // LED ON
    }

  • これは実際の信号波形やマスタ側のI2Cコードの確認も必要な気がします。マスタ側もGR-SAKURAでWireライブラリを使っていますか?

    ぶっちゃけ、マイコン間通信はUARTでいいような気がします。そういう意味でWireのスレーブモードは使ったことがありません。

Reply
  • これは実際の信号波形やマスタ側のI2Cコードの確認も必要な気がします。マスタ側もGR-SAKURAでWireライブラリを使っていますか?

    ぶっちゃけ、マイコン間通信はUARTでいいような気がします。そういう意味でWireのスレーブモードは使ったことがありません。

Children
No Data