Arduino Due - TWI I2C - MultiMasterMode - Repeated Start Problems.

Hi all, first of all I'm sorry for my bad English, try to forgive me in advance for my mistakes...

I have several Arduino but at the moment my problems are with Arduino DUE. For my project I'm trying to build an "Arduino grid" so I have several Arduinos that work toghether, using I2C bus for communications. I already use asynchronous communication but now I want to implement some "synchronous services" so:

My Arduino Due is in this moment acting as a "MASTER". I need to send an I2C message to another specific Arduino (acting as a slave in this moment), wait and read the answer. Between the WRITE (TX) and READ(RX) I don't want to release the bus, that must be for exclusive use of my "DUE Master" and that specific other one. (Uno Micro or Mini). To do so I can't send a "STOP" after the end of the TX step, but I need a "repeated START" that continues to hold the bus. I'm able to do that with the other Arduinos but not with DUE.

I've already read some threads and the use of "SAM3X IAddress Mode" is not an option because I need to send a message of several bytes not just 1 or 3.

Can you help me please? Has anyone been able to do a proper "Repeated Start" on the I2C bus with Arduino DUE? Thank you in advance. Luca.

PS: I have already re-wrote the "Wire" library and read the SAM3X processor specifications.

I post some example code, if someone feels like to play with… he’s welcome. Please, let me know if you think I’m making conceptual mistakes… I tried to go “low level” avoiding the use of Wire.h or twi.h functions, just to gain more control on what’s happening.

In the MASTER code I made a comment where the STOP occurs (between WRITE and READ) while I want REPEATED START. Try to substitute TWI_CR_STOP with TWI_CR_START and see what happens.

There are two pieces of code, one for Arduino DUE (acting as a MASTER) and the other one for Arduino MINI (acting as a SLAVE). Remind that Arduino DUE works on 3.3V, that’s why I chose Arduino MINI 3.3V as a slave. The slave code works also with Arduino MICRO or UNO(5V), but you will need a logical level translator on the I2C bus.

I work with two external pull-up 10KOhm resistors on the bus lines.
I usually test this pieces of code releasing SLAVE reset button first, waiting for the “MINI” line on the serial and then releasing MASTER reset button.

I use Eclipse as IDE, so some little adjustment may be needed for the code to be compiled on Arduino IDE.
Here’s the MASTER code:

#include "I2CSimpleDue.h"

// TWI clock frequency
static const uint32_t TWI_CLOCK = 400000;

Twi *twi = WIRE_INTERFACE;

void setup()
{
    Serial.begin(115200);

    pmc_enable_periph_clk(WIRE_INTERFACE_ID);
    PIO_Configure(
    	g_APinDescription[PIN_WIRE_SDA].pPort,
    	g_APinDescription[PIN_WIRE_SDA].ulPinType,
    	g_APinDescription[PIN_WIRE_SDA].ulPin,
    	g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
    PIO_Configure(
    	g_APinDescription[PIN_WIRE_SCL].pPort,
    	g_APinDescription[PIN_WIRE_SCL].ulPinType,
    	g_APinDescription[PIN_WIRE_SCL].ulPin,
    	g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);

    NVIC_DisableIRQ(TWI1_IRQn);
    NVIC_ClearPendingIRQ(TWI1_IRQn);
    NVIC_SetPriority(TWI1_IRQn, 0);
    NVIC_EnableIRQ(TWI1_IRQn);

    // Disable PDC channel
    twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
    TWI_ConfigureMaster(twi, TWI_CLOCK, VARIANT_MCK);
}

byte x = 0;
char txBuffer[5] = {'x',' ','i','s',' '};
uint8_t txBufferLength = 5;
char rxBuffer[32];

void loop()
{
 	//TWI_StartWrite(twi, 0x15, 0, 0, txBuffer[0]);
	//----------------------------------------------------------------
	/* Set slave address and number of internal address bytes. */
    twi->TWI_MMR = 0;
    twi->TWI_MMR = (0 << 8) | (0x15 << 16);

    /* Set internal address bytes. */
    twi->TWI_IADR = 0;

    /* Write first byte to send.*/
    //TWI_WriteByte(twi, txBuffer[0]);
    twi->TWI_THR = txBuffer[0];
	//----------------------------------------------------------------

    //bool _startError = !TWI_WaitByteSent(twi, XMIT_TIMEOUT);
    //----------------------------------------------------------------
    while(!((twi->TWI_SR & TWI_SR_TXRDY) == TWI_SR_TXRDY)){
    	if ((twi->TWI_SR & TWI_SR_NACK) == TWI_SR_NACK){
    		Serial.println("START ERROR");
    		break;
    	}
    }
    //----------------------------------------------------------------
    int sent = 1;

    bool _writeError = false;
    while (sent < txBufferLength && !_writeError) {
      //TWI_WriteByte(twi, txBuffer[sent++]);
      //--------------------------------------------------------------
      twi->TWI_THR = txBuffer[sent++];
      //--------------------------------------------------------------

      //_writeError = !TWI_WaitByteSent(twi, XMIT_TIMEOUT);
      //--------------------------------------------------------------
      while(!((twi->TWI_SR & TWI_SR_TXRDY) == TWI_SR_TXRDY)){
      	if ((twi->TWI_SR & TWI_SR_NACK) == TWI_SR_NACK){
      		Serial.println("WRITE ERROR");
      		_writeError = true;
      		break;
      	}
      }
      //--------------------------------------------------------------
    }

   	//TWI_Stop(twi);  //Sending STOP before sending LAST BYTE
    //----------------------------------------------------------------
    //THIS IS THE POINT WHERE I WANT THE REPEATED START
    twi->TWI_CR =  TWI_CR_STOP;
    //----------------------------------------------------------------
    //TWI_WriteByte(x);
   	//----------------------------------------------------------------
    twi->TWI_THR = x;
    //----------------------------------------------------------------

    //if(!TWI_WaitTransferComplete(twi, XMIT_TIMEOUT)){
    //	Serial.println("WRITE STOP ERROR!");
    //}
    //----------------------------------------------------------------
   	while (!((twi->TWI_SR & TWI_SR_TXCOMP) == TWI_SR_TXCOMP)){
      	if ((twi->TWI_SR & TWI_SR_NACK) == TWI_SR_NACK){
      		Serial.println("WRITE STOP ERROR");
      		break;
      	}
	}
   	//----------------------------------------------------------------

   	/*twi->TWI_MMR = 0;
    twi->TWI_MMR = (0 << 8) | TWI_MMR_MREAD | (0x15 << 16);
    twi->TWI_IADR = 0;
    twi->TWI_CR = TWI_CR_START | TWI_CR_STOP;
    //twi->TWI_THR;*/

    //Comment or less delay - it doesn't work: WHY?
    delay(10);

    /*uint32_t rc;
    for (int i = 0;i<10000;i++){
    	rc = TWI_GetStatus(twi);
    	if (rc != 61448){
			Serial.print("TWI Status:");
			Serial.println("rc:");
			Serial.println(rc);
			Serial.print("TXCOMP:");
			Serial.println(TWI_STATUS_TXCOMP(rc));
			Serial.print("RXRDY:");
			Serial.println(TWI_STATUS_RXRDY(rc));
			Serial.print("TXRDY:");
			Serial.println(TWI_STATUS_TXRDY(rc));
			Serial.print("SVREAD:");
			Serial.println(TWI_STATUS_SVREAD(rc));
			Serial.print("SVACC:");
			Serial.println(TWI_STATUS_SVACC(rc));
			Serial.print("GACC:");
			Serial.println(TWI_STATUS_GACC(rc));
			Serial.print("NACK:");
			Serial.println(TWI_STATUS_NACK(rc));
			Serial.print("ARBLST:");
			Serial.println(TWI_STATUS_ARBLST(rc));
			Serial.print("EOSACC:");
			Serial.println(TWI_STATUS_EOSACC(rc));

			if(TWI_STATUS_RXRDY(rc)){
				char data = twi->TWI_RHR;
				Serial.print("DATA:");
				Serial.println(data);
			} else {
				Serial.print("TWI Status:");
				Serial.println(rc);
			}
    	}
    	delay(10);
    }*/

    // Start Read
    uint8_t readed = 0;
    uint8_t quantity = 10;
    //TWI_StartRead(twi, 0x15, 0, 0);
   	//----------------------------------------------------------------
    /* Set slave address and number of internal address bytes. */
    twi->TWI_MMR = 0;
    twi->TWI_MMR = (0 << 8) | TWI_MMR_MREAD | (0x15 << 16);

    /* Set internal address bytes */
    twi->TWI_IADR = 0;

    /* Send START condition */
    twi->TWI_CR = TWI_CR_START;
   	//----------------------------------------------------------------


    do {
      // Stop condition must be set during the reception of last byte
       if (readed == quantity -1){
    	  //TWI_SendSTOPCondition(twi);
    	  //----------------------------------------------------------
    	  twi->TWI_CR |= TWI_CR_STOP;
    	  //----------------------------------------------------------
    	  Serial.println();
    	  Serial.println("Stop Condition Sent.");
      }

      //TWI_WaitByteReceived(twi, RECV_TIMEOUT);
      //--------------------------------------------------------------
		while (!((twi->TWI_SR & TWI_SR_RXRDY) == TWI_SR_RXRDY)) {
	      	if ((twi->TWI_SR & TWI_SR_NACK) == TWI_SR_NACK){
	      		Serial.println("READ ERROR");
	      		break;
	      	}
	    }
      //--------------------------------------------------------------

      //rxBuffer[readed] = TWI_ReadByte(twi);
      //--------------------------------------------------------------
		rxBuffer[readed] = twi->TWI_RHR;
      //--------------------------------------------------------------

      if (readed == 9){
    	  Serial.println((uint8_t)rxBuffer[readed]);
      } else {
    	  Serial.print(rxBuffer[readed]);
      }
      readed++;
     } while (readed < quantity);


    //if(!TWI_WaitTransferComplete(twi, RECV_TIMEOUT)){
    //	Serial.println("READ STOP ERROR!");
    //}
    //----------------------------------------------------------------
   	while (!((twi->TWI_SR & TWI_SR_TXCOMP) == TWI_SR_TXCOMP)){
      	if ((twi->TWI_SR & TWI_SR_NACK) == TWI_SR_NACK){
      		Serial.println("READ STOP ERROR");
      		break;
      	}
	}
   	//----------------------------------------------------------------
    x++;

    delay(10000);
}

… to be continued …

And here’s the SLAVE code:

#include "I2CSimpleMini.h"

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define I2CMM_STA_NO_RELEVANT_INFO 0xF8
#define I2CMM_STA_BUS_ERROR 0x00
#define I2CMM_STA_START_TRANSMITTED 0x08
#define I2CMM_STA_REPEATED_START_TRANSMITTED 0x10
#define I2CMM_STA_MW_SLAW_TRANSMITTED_ACK 0x18
#define I2CMM_STA_MW_SLAW_TRANSMITTED_NACK 0x20
#define I2CMM_STA_MW_DATA_TRANSMITTED_ACK 0x28
#define I2CMM_STA_MW_DATA_TRANSMITTED_NACK 0x30
#define I2CMM_STA_ARBITRATION_LOST 0x38
#define I2CMM_STA_MR_SLAR_TRANSMITTED_ACK 0x40
#define I2CMM_STA_MR_SLAR_TRANSMITTED_NACK 0x48
#define I2CMM_STA_MR_DATA_RECEIVED_ACK 0x50
#define I2CMM_STA_MR_DATA_RECEIVED_NACK 0x58
#define I2CMM_STA_SR_SLAW_RECEIVED_ACK 0x60
#define I2CMM_STA_SR_PAL_SLAW_RECEIVED_ACK 0x68
#define I2CMM_STA_SR_GCALL_RECEIVED_ACK 0x70
#define I2CMM_STA_SR_PAL_GCALL_RECEIVED_ACK 0x78
#define I2CMM_STA_SR_DATA_RECEIVED_ACK 0x80
#define I2CMM_STA_SR_DATA_RECEIVED_NACK 0x88
#define I2CMM_STA_SR_GCALL_DATA_RECEIVED_ACK 0x90
#define I2CMM_STA_SR_GCALL_DATA_RECEIVED_NACK 0x98
#define I2CMM_STA_SR_STOP_RECEIVED 0xA0
#define I2CMM_STA_SW_SLAR_RECEIVED_ACK 0xA8
#define I2CMM_STA_SW_PAL_SLAR_RECEIVED_ACK 0xB0
#define I2CMM_STA_SW_DATA_TRANSMITTED_ACK 0xB8
#define I2CMM_STA_SW_DATA_TRANSMITTED_NACK 0xC0
#define I2CMM_STA_SW_LAST_DATA_TRANSMITTED_ACK 0xC8

uint8_t _bytesReceived = 0;
uint8_t _bytesSent = 0;
uint8_t acmInterrupt = 0;
uint8_t x;
char _sendBuffer[9] = {'R','e','c','e','i','v','e','d',':'};

//The setup function is called once at startup of the sketch
void setup()
{
    Serial.begin(115200);    // start serial for output
    Serial.println("MINI");

	TWAR = (0x15 << 1) | 1;

	//activate internal pullups for twi.
	digitalWrite(SDA, 1);
	digitalWrite(SCL, 1);

	cbi(TWSR, TWPS0);
	cbi(TWSR, TWPS1);
	TWBR = ((F_CPU / 400000L) - 16) / 2;

	TWCR = _BV(TWINT)|_BV(TWEA)|_BV(TWEN)|_BV(TWIE);
}

// The loop function is called in an endless loop
void loop()
{
	if (acmInterrupt){
		uint8_t _rc = acmInterrupt;
		acmInterrupt = 0;

		switch(_rc){
		case(I2CMM_STA_SR_SLAW_RECEIVED_ACK):{
			Serial.println("SR_SLAW_ACK");
			_bytesReceived = 0;
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case (I2CMM_STA_SR_DATA_RECEIVED_ACK):{
			//Serial.println("SR_DATA_ACK");
			char data = TWDR;
			_bytesReceived++;
			if (_bytesReceived == 6){
				x = (uint8_t)data;
				Serial.println((uint8_t)data);
			} else {
				Serial.print(data);
			}
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case(I2CMM_STA_SR_STOP_RECEIVED):
		{
			Serial.println("SR_STOP_RECEIVED");
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case(I2CMM_STA_SW_SLAR_RECEIVED_ACK):{
			Serial.println("SW_SLAR_ACK");
			TWDR = _sendBuffer[0];
			Serial.print(_sendBuffer[0]);
			_bytesSent = 1;
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case(I2CMM_STA_SW_DATA_TRANSMITTED_ACK):{
			//Serial.println("SW_DATA_ACK");
			if (_bytesSent < 9){
				TWDR = _sendBuffer[_bytesSent];
				Serial.print(_sendBuffer[_bytesSent]);
				TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			} else {
				TWDR = x;
				Serial.println((uint8_t)x);
				TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWIE);
			}
			_bytesSent++;
			break;

		}

		case(I2CMM_STA_SW_DATA_TRANSMITTED_NACK):{
			Serial.println("SW_DATA_TX_NACK");
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case(I2CMM_STA_SW_LAST_DATA_TRANSMITTED_ACK):{
			Serial.println("SW_LAST_DATA_ACK");
			TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWEA)|_BV(TWIE);
			break;
		}

		case(I2CMM_STA_NO_RELEVANT_INFO):{
			break;
		}

		default:
			Serial.print("INT NOT MANAGED:");
			Serial.println(_rc);
		}

	}
}

ISR(TWI_vect)
{
	acmInterrupt = (TWSR & 0xF8);
}

Thank you all in advance.
Luca.