I2CSlaveで2回連続したWriteAddressedがうまく分割判定できない

18 Aug 2019

I2CSlaveで、2回連続したWriteAddressedがうまく分割判定できず、困っています。

  • 2つのWriteAddressedが1つになってしまう
  • アドレスを示す0x40がデータとして取れてしまう

I2CSlaveの使い方に問題があるのでしょうか。 アドバイスをよろしくお願い致します。

使用ボード:TMPM066

I2CSlaveコード(Mbed5)

#include <mbed.h>

#define DLM	"\r\n"

#define I2C_SLAVE_ADDRESS	(0x40)

static I2CSlave _I2c(MBED_I2C0);

static void I2cSlaveDoWork()
{
	int addressed = _I2c.receive();
	if (addressed == I2CSlave::NoData) return;

	printf("Enter I2C Slave" DLM);

	switch (addressed)
	{
	case I2CSlave::WriteAddressed:
		for (int i = 0; ; i++)
		{
			int data = _I2c.read();
			printf("%#02x" DLM, data);

			if (data < 0) break;
		}
		break;
	case I2CSlave::ReadAddressed:
		if (_I2c.write("ABC", 3) != 0) return;
		break;
	}

	printf("Leave I2C Slave" DLM);
}

int main()
{
	_I2c.address(I2C_SLAVE_ADDRESS);
	while (true)
	{
		I2cSlaveDoWork();
	}
}

I2CMasterコード (Arduino Uno)

#include <Wire.h>

void setup()
{
  Wire.begin();
}

void loop()
{
  Wire.beginTransmission(0x20);
  Wire.write(0x12);
  Wire.endTransmission();

  Wire.beginTransmission(0x20);
  Wire.write(0x34);
  Wire.write(0x56);
  Wire.write(0x78);
  Wire.endTransmission();
  delay(1000);
}

/media/uploads/matsujirushi/20190816a.png

実行時のログ出力 (Mbed5)

Enter I2C Slave
0x12
0x40
0x34
0x56
0x78
0xffffffff
Leave I2C Slave
Enter I2C Slave
0x12
0x40
0x34
0x56
0x78
0xffffffff
Leave I2C Slave
19 Aug 2019

複数バイトの送受信は、I2CSlave::read(char *data, int length) を使うのが適切のような気がするので、以下のように変更して試してみました。

static void I2cSlaveDoWork()
{
    // clear buffer
    for (int i = 0; i<10; i++) {
        buf[i] = 0;
    }

    int addressed = _I2c.receive();
    if (addressed == I2CSlave::NoData) return;
    int data;

    pc.printf("Enter I2C Slave" DLM);

    switch (addressed) {
        case I2CSlave::WriteAddressed:
            data = _I2c.read(buf, 10);
            if (data != 0) {
                pc.printf("*** Received %d bytes\n", data);
            }
            break;
        case I2CSlave::ReadAddressed:
            if (_I2c.write("ABC", 3+1) != 0) return;
            break;
        default:
            break;
    }

    int i = 0;
    while(buf[i] != 0) {
        pc.printf("%#02x" DLM, buf[i++]);
    }
    
    
    pc.printf("Leave I2C Slave" DLM);
}

TMPM066では、以下のログが得られました(松岡さんのログと同じ)。

Enter I2C Slave
*** Received 1 bytes
0x12
0x40
0x34
0x56
0x78
Leave I2C Slave
Enter I2C Slave
*** Received 1 bytes
0x12
0x40
0x34
0x56
0x78
Leave I2C Slave

また、LPC1768で同じコードを動作させたところ、以下のログが得られました(二つ目の書き込みデータが読めていません)。

Enter I2C Slave
*** Received 1 bytes
0x12
Leave I2C Slave
Enter I2C Slave
*** Received 1 bytes
0x12
Leave I2C Slave

I2CSlave側の実装はポーリングで行われていると思うので、マスタから送信するタイミングにも依存するのかも知れません。

19 Aug 2019

試していただき、ありがとうございます!

LPC1768は良さそうな挙動で、TMPM066は問題が内在していそうな気がするのですが、この認識で合っていますか?

  • LPC1768は、2回目のbeginTransmission()前にdelayすれば正しく受信できそうな予感。
  • TMPM066は、アドレスがデータとして読み取られている模様。
20 Aug 2019

Master側のプログラムにdelayを追加して試してみました。

#include <Wire.h>
 
void setup()
{
  Wire.begin();
}
 
void loop()
{
  Wire.beginTransmission(0x20);
  Wire.write(0x12);
  Wire.endTransmission();
  delay(1000);
 
  Wire.beginTransmission(0x20);
  Wire.write(0x34);
  Wire.write(0x56);
  Wire.write(0x78);
  Wire.endTransmission();
  delay(1000);
}

LPC1768, TMPM066どちらも以下のログが得られました。

*** Received 1 bytes
0x12
Leave I2C Slave
Enter I2C Slave
*** Received 1 bytes
0x34
0x56
0x78
Leave I2C Slave

TMPM066の方は「アドレスがデータとして読み取られている」というよりも、「ポーリング処理が完了しない時に内部ステートが想定しない状態になって、データの取り扱いが不正になった」という感じだと思います。