puuu / USIWire

USI based TWI/I2C library for Arduino
GNU Lesser General Public License v3.0
60 stars 18 forks source link

Switching between master and slave role on ATTiny85 fails. #5

Closed x821938 closed 6 years ago

x821938 commented 6 years ago

I have a sketch on my attiny where I need to change between being an i2c master and a slave. This doesn't work.

If I only work as a master and poll some sensors - it works fine. If I only work as a slave and get polled from another master it works fine.

If I want the functionality of being both a master and a slave (eg switching role with a button on the attiny), then the slave fails. I tried the Wire.end() when I want to go from being slave to master, but without luck.

Any ideas?

Best regards, Alex.

puuu commented 6 years ago

Switching between master and slave mode is an interesting question. I never tried it. Simultaneous operation of master and slave mode is impossible because of the shared buffers. That is why it is important to stop the slave mode before using master mode, otherwise the interrupt routine will overwrite the data in the buffer.

Theoretically, it should work like this:

Wire.onRequest(requestEvent);
Wire.onReceive(receiveEvent);

// master mode
Wire.begin()
Wire.beginTransmission()
Wire.write(slave_addr);
Wire.write(data1);
Wire.endTransmission()

// slave mode
Wire.begin(this_slave_addr) // this enables the slave interrupts
// doing the slave stuff and decide to switch the mode
Wire.end() // this disables the slave interrupts

// master mode
Wire.begin()
Wire.beginTransmission()
Wire.write(slave_addr);
Wire.write(data2);
Wire.endTransmission()

Please report if this is working. If not, please provide a minimal example and describe the problem in detail.

x821938 commented 6 years ago

Hi,

Thanks for getting back to me so fast :) I made a small example with an ATTiny85 connected to a VEML6070 light sensor and an Arduino Pro-Mini via I2C bus.

I made an absulute minimal code for both the Pro-Mini and the ATTiny. It can be found here: https://github.com/x821938/test_usi_i2c_master_slave_switch

The Attiny starts as a slave. If it receives the command byte 'L'. It:

  1. Ends slave mode
  2. Becomes a master and gets light data from VEML6070 UV sensor
  3. Enters slave mode again

The Pro-Mini waits a little after sending the 'L' command and then pull the light data from the ATTiny slave.

If I comment out the part of the Pro-Mini code where I send the 'L' command, then the ATTiny slave nicely talks back to me. Just sending zeroes (obviousely, because it never reads from sensor). But as soon as the ATTiny has switched to master mode, I get 255 values from it. (because of missing error handling - but I am trying to keep the code minimal for demonstration purposes)

Likewise I have also tested the ATTiny talking to the lightsensor - without ever enabling slave mode. Via the visual feedback led, I can prove that it also works fine by itself.

I hope the example makes sense.

Best regards, Alex

puuu commented 6 years ago

Hi, the example code is clear to me. But I did not yet get, why you get 255. Could it be, that the ATTiny did not release the SDA line correctly, e.g., does Pro-Mini read also 255 from the light sensor. Did you can check the voltage of the SDA line? A call of readFromLightSensor(); in setup() of the Pro-Mini can also help to investigate this problem.

x821938 commented 6 years ago

Here is output from my datalogger when attiny behaves good and wrong.

alt text alt text

If that can help.

x821938 commented 6 years ago

Here is the output on my Pro-Mini:

Sending master a command: L
Giving him a little time to finish his job
We got light value from I2C slave: 255
We got light value directly from sensor: 0

Sending master a command: L
Giving him a little time to finish his job
We got light value from I2C slave: 255
We got light value directly from sensor: 0

Sending master a command: L
Giving him a little time to finish his job
We got light value from I2C slave: 255
We got light value directly from sensor: 6

When Wire.requestFrom fails to get data, it always return 255. I think the attiny releases the SDA fine. The Pro-mini reads the value fine. See output.

x821938 commented 6 years ago

Anoher dump.

Wrong: alt text

Good: alt text

x821938 commented 6 years ago

Hi again,

I found the mistake in your library :) You forget to set values in the begin method for the slave.... When I change it to this, it works:

void USIWire::begin(uint8_t address) {
  BufferIndex = 0;
  BufferLength = 0;
  transmitting = 0;
  USI_TWI_Slave_Initialise(address);
}

If you only run slave mode it works because the variables get set in the inital declaration.

Best regards, Alex

puuu commented 6 years ago

Thank you for the information.

The missing reset of the variables in USIWire::begin are definitely a bug. Please feel free to submit a pull request.

But, I did not understand, why this cause the problem with the 255 value. If the variables are not set to 0 during WIRE.begin(), then BufferIndex and BufferLength differ from 0 after the call of Wire.requestFrom(). This leads to a not correctly working Wire.read() in slave mode. For your code on the ATTiny this means:

The dumps of your logic analyser show that the USIWire work correctly in slave mode, e.g., changes of the SDA signal for ACK.

Does the ATTiny still report 255 with you patch? If yes, can you provide a dump that show the 'L' request and the subsequent master mode of the ATTiny?

x821938 commented 6 years ago

When the attiny failed to switch from master to slave, it never answered when the pro mini was polling for data. This means that SDA was high all the time the the master was communicating. Because I didn't have error handling on my pro mini, the received value will be 255 because it's latching in binary ones on each SCL clock pulse.

puuu commented 6 years ago

Closed with #6 .