Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C in slave mode #36

Open
dpaxson opened this issue Jul 13, 2018 · 4 comments
Open

I2C in slave mode #36

dpaxson opened this issue Jul 13, 2018 · 4 comments

Comments

@dpaxson
Copy link

dpaxson commented Jul 13, 2018

Hi,

I'm trying to set up a Butterfly to act as an I2C slave device. It receives data just fine but trying to send data back in the request callback just ends up having 0's be received on the other end.

I think what is happening is when the callback calls Wire.write, write checks to see if _tx_active is true. If it isn't true, it just early exits, returning 0.

I think one way this could be addressed would be to set _tx_active before calling the request callback from TwoWire::EventCallback and then setting it false again afterwards.

So instead of:

	if (events & I2C_EVENT_TRANSMIT_REQUEST) {
	    _tx_write = 0;

	    if(_requestCallback) {
		(*_requestCallback)();
	    }
      
	    stm32l4_i2c_service(_i2c, &_tx_data[0], _tx_write);
	}

Have something like this:

	if (events & I2C_EVENT_TRANSMIT_REQUEST) {
	    _tx_write = 0;
	    _tx_active = true;

	    if(_requestCallback) {
		(*_requestCallback)();
	    }
      
	    _tx_active = false;

	    stm32l4_i2c_service(_i2c, &_tx_data[0], _tx_write);
	}
@GrumpyOldPizza
Copy link
Owner

GrumpyOldPizza commented Jul 13, 2018 via email

@dpaxson
Copy link
Author

dpaxson commented Jul 13, 2018

So, yes, I ended up pulling down the code and applied the change and now I am sending bytes out when in slave mode. It looks like it is working.

@dpaxson
Copy link
Author

dpaxson commented Jul 23, 2018

So I ended up getting a logic analyzer and have been looking at the I2C communications.

I see an I2C write with a single byte (the register to do a read from) followed later by a read. The first pair returns the correct data. The second register write comes through (for a different register) and then the read request. In this case, the same data is returned as for the first request even though it is supposed to be different data. From this second register write forward, it seems that the data that is received by the I2C slave is lagging by one byte. So the 3rd byte received is really the 2nd one that should have been received. The 4th received is really the 3rd that should have been received and so on.

The logic in my program looks to be correct. It looks like there are extra onRequest callback calls being made from the Wire library, however. And there appears to be an onReceive callback call with 0 bytes received before the second register write is received.

Would you have any ideas what might be causing this behavior?

@larjohn
Copy link

larjohn commented Mar 8, 2019

I 've got a probably related issue with slave I2C. My setup is using a Raspberry Pi Zero W as a master (using pigpio) and a Butterfly equivalent as a slave. Whenever the master is requesting a single byte from the slave, everything is working as expected. If the slave outputs for instance 4 bytes, and the master only requests a single byte, again, everything works.

The issue arises when the slave is requested to write multiple bytes and the master reads all of them. This works the first time: all 4 bytes are transfered successfully. The next time, though, it breaks: the i2cdetect utility can't detect the slave anymore - it needs a board reset to find it. The bytes requested cannot be read by the master (only gets an empty array).

Interestingly enough, while trying to debug it, I modified the TwoWire::EventCallback method like this:

void TwoWire::EventCallback(uint32_t events)
{
    unsigned int status;
    void(*callback)(uint8_t);

    if (_option & I2C_OPTION_ADDRESS_MASK) {
	if (events & I2C_EVENT_RECEIVE_DONE) {
	    _rx_read = 0;
	    _rx_write = stm32l4_i2c_count(_i2c);
	  
	    if(_receiveCallback) {
		(*_receiveCallback)(_rx_write);
	    }
      
	    stm32l4_i2c_service(_i2c, &_rx_data[0], BUFFER_LENGTH);
	}
    
	if (events & I2C_EVENT_RECEIVE_REQUEST) {
	    stm32l4_i2c_service(_i2c, &_rx_data[0], BUFFER_LENGTH);
	}
    
	if (events & I2C_EVENT_TRANSMIT_REQUEST) {
	    _tx_write = 0;
	    _tx_active = true;

	    if(_requestCallback) {
		(*_requestCallback)();
	    }

	    _tx_active = false;


        for(int i = 0; i < BUFFER_LENGTH; i++)
        {
            Serial.println(_tx_data[i]);
        }



	    stm32l4_i2c_service(_i2c, &_tx_data[0], _tx_write);
	}
    } else {
	if (events & (I2C_EVENT_ADDRESS_NACK | I2C_EVENT_DATA_NACK | I2C_EVENT_ARBITRATION_LOST | I2C_EVENT_BUS_ERROR | I2C_EVENT_OVERRUN | I2C_EVENT_RECEIVE_DONE | I2C_EVENT_TRANSMIT_DONE | I2C_EVENT_TRANSFER_DONE)) {
	    callback = _completionCallback;
	    _completionCallback = NULL;

	    if (callback) {
		if (!(events & (I2C_EVENT_ADDRESS_NACK | I2C_EVENT_DATA_NACK | I2C_EVENT_ARBITRATION_LOST | I2C_EVENT_BUS_ERROR | I2C_EVENT_OVERRUN))) {
		    status = 0;
		} else {
		    if (events & I2C_EVENT_ADDRESS_NACK) {
			status = 2;
		    } else if (events & I2C_EVENT_DATA_NACK) {
			status = 3;
		    } else {
			status = 4;
		    }
		} 
	
		(*callback)(status);
	    }
	}
    }
}

In other words, I just serial print the TX buffer to see what is inside. With that modification, consecutive writes work correctly - which is not expected: the print buffer has to be larger (64 bytes?) than the TX buffer, so the print operation shouldn't be blocking at all, to cause any convenient delay and justify the behavior, implying a race condition.

Please note that, if the master only requests single bytes after the initial multibyte request, it works without modification. Only consecutive multibyte write requests seem to be completely shutting down the I2C channel. I couldn't figure out if this shut-down is handled in the software or if the hardware somehow partially transitions to an unrecoverable state (other parts of my sketch keep working).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants