Wire onReceive(handler) issue

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Thu Feb 07, 2013 10:01 pm

Hi!

Thank you for your answer and feel free to release betas so we can test them.

Good luck with the development!

mkirby
Posts: 13
Joined: Thu Oct 18, 2012 11:30 pm

Re: Wire onReceive(handler) issue

Post by mkirby » Tue Feb 12, 2013 3:01 am

The issue unfortunately is inherent to how the interrupt system works for the I2C bus on pic32 microcontrollers. There is no stop bit received interrupt for the slave side of transmission. This means that data reception cannot be driven by an interrupt routine because there's no way to know when transmission is complete. To counter for this, the original library was built to only allow for one data byte transmission. The problem can be fixed by doing away with the interrupt driven method and instead writing a function for it, but this would cause the program to block when waiting for a message. In short, no solution to this problem is going to solve everything, so we're trying to decide which issues to tackle with the updated library.

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Tue Feb 12, 2013 5:55 am

Thank you for the update. It seems to be really tricky.

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Sun Mar 24, 2013 4:28 pm

Hi!

Any news on this issue?

Sadly, it isn't included in the priority list available at Get involved with the chipKIT development community :(

Thanks.

User avatar
Jacob Christ
Posts: 723
Joined: Sat May 28, 2011 6:34 am
Location: Southern California
Contact:

Re: Wire onReceive(handler) issue

Post by Jacob Christ » Sat Feb 08, 2014 4:36 pm

I've created a fix that allows chipKIT I2C to behave like Atmel Arduino I2C with stop bit interrupt. The workaround comes at the cost of burning a few hundred CPU cycles in the slave interrupt at the end of each received byte to wait for detection of the stop bit. There is also the possibility of missing a stop bit if it comes later than the wait. I've tested this in MPIDE version 20130715 using two Fubarino SD's .

If some other people can verify that this doesn't break there code and solves the problem I think we can get it rolled into the chipKIT core.

Jacob

The modification to twi.c that is needed to make this work is here:

Code: Select all

		case TW_SR_DATA:
			//twi_rxBuffer[twi_rxBufferIndex] = (uint8_t)((ptwi->ixStat.reg & 0xff00) >> 8);
			//twi_rxBufferIndex++;
			//twi_rxBuffer[twi_rxBufferIndex] = (uint8_t)(ptwi->ixStat.reg & 0x00ff);
			//twi_rxBufferIndex++;
			
			if( twi_rxBufferIndex < TWI_BUFFER_LENGTH )
			{
				twi_rxBuffer[twi_rxBufferIndex] = ptwi->ixRcv.reg;
				twi_rxBufferIndex++;
			}
			
			// Release clock line
			ptwi->ixCon.set = (1 << _I2CCON_SCLREL);
			ptwi->ixStat.clr = (1 << _I2CSTAT_I2COV) | (1 << _I2CSTAT_RBF);

			// Burn a few hundred cycles to wait for the stop condition
			// (burns about four cycles per iteration)
			int i;
			for(i = 0; i < 250; i++)
			{
				asm volatile("nop");
			}

			// If the stop flag is set then invoke the twi_onSlaveRecieve callback
			if(ptwi->ixStat.reg & (1 << _I2CSTAT_P) )
			{
				twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
				twi_rxBufferIndex = 0; // Reset the index to zero
			}

			break;

The send code:

Code: Select all

#include <Wire.h>

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(115200);           // start serial for output
  delay(3000);
  Serial.println("sender...");
}

void loop()
{
  Serial.println("sending...");
  Wire.beginTransmission(55); // transmit to device #55
  Wire.send("012345678901234567890123456789");        // sends five bytes
  Wire.endTransmission();    // stop transmitting

  delay(1000);
}
The receive code (without exposed I2C register):

Code: Select all

#include <Wire.h>
char c = 0;

void setup()
{
  Wire.begin(55);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent);  // register event
  Serial.begin(115200);           // start serial for output
  delay(3000);
  Serial.println("receiver...");
}

void loop()
{
  delay(100);
}

void receiveEvent(int howMany)
{
  Serial.print(howMany);
  Serial.print(" ");
  while(Wire.available()) // loop through all but the last
  {
    c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  Serial.println(" ");         // print the character
}
The receive code (with some an exposed I2C register):

Code: Select all

#include <p32xxxx.h>
#include <pins_arduino.h>
#include <p32_defs.h>
#include <Wire.h>
char c = 0;

static p32_i2c *	ptwi = (p32_i2c *)_I2C1_BASE_ADDRESS;

void setup()
{
  Wire.begin(55);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent);  // register event
  Serial.begin(115200);           // start serial for output
  delay(3000);
  Serial.println("receiver...");
}


void loop()
{
  static int state = 0;

  if(state == 0 && ptwi->ixStat.reg & (1 << _I2CSTAT_S) )
  {
    Serial.println("start");
    state = 1;
  }
  if(state == 1 && ptwi->ixStat.reg & (1 << _I2CSTAT_P) )
  {
    Serial.println("stop");
    state = 0;
  }
}

void receiveEvent(int howMany)
{
  Serial.print(howMany);
  Serial.print(" ");
  while(Wire.available()) // loop through all but the last
  {
    c = Wire.receive(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  Serial.println(" ");         // print the character
}
PONTECH Quick240 an industrial form factor platform for chipKIT and Arduino users.

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Sat Feb 08, 2014 4:46 pm

Thank you! I'll test the code.

:)

User avatar
Jacob Christ
Posts: 723
Joined: Sat May 28, 2011 6:34 am
Location: Southern California
Contact:

Re: Wire onReceive(handler) issue

Post by Jacob Christ » Sat Feb 08, 2014 5:02 pm

If the stop bit is not being detected try to bump up the number of itterations in the delay loop:

Code: Select all

         // Burn a few hundred cycles to wait for the stop condition
         // (burns about four cycles per iteration)
         int i;
         for(i = 0; i < 250; i++)
         {
            asm volatile("nop");
         }
Jacob
PONTECH Quick240 an industrial form factor platform for chipKIT and Arduino users.

User avatar
Jacob Christ
Posts: 723
Joined: Sat May 28, 2011 6:34 am
Location: Southern California
Contact:

Re: Wire onReceive(handler) issue

Post by Jacob Christ » Wed Feb 12, 2014 7:01 pm

So the other day I had a concern that this fix might not work if two messages came in really fast (as the delay in looking for the stop bit in the interrupt may be too long if two messages came in really fast).... To test my concern I modified the send program to transmit two messages really fast then delay for 1 second. The good news is that this didn't break the patch.

Jacob

Code: Select all

#include <Wire.h>

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(115200);           // start serial for output
  delay(3000);
  Serial.println("sender...");
}

void loop()
{
  Serial.println("sending...");
  Wire.beginTransmission(55); // transmit to device #55
  Wire.send("Hello");
  Wire.endTransmission();    // stop transmitting
  Wire.beginTransmission(55); // transmit to device #55
  Wire.send("World!");
  Wire.endTransmission();    // stop transmitting

  delay(1000);
}
PONTECH Quick240 an industrial form factor platform for chipKIT and Arduino users.

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Mon Nov 17, 2014 6:39 pm

I went back to my project based on I²C and tried the solution. It works!

The only modification on the slave side was the number of cycles.

Code: Select all

// Burn a few hundred cycles to wait for the stop condition
// (burns about four cycles per iteration)
long i;
for (i = 0; i < 290; i++)
{
    asm volatile("nop");
}
On the master side, an Arduino Uno, a short delay was needed between two consecutive sendings.

Code: Select all

delay(20);
Thank you!

avenue33
Site Admin
Posts: 298
Joined: Sat Jul 16, 2011 10:19 am
Contact:

Re: Wire onReceive(handler) issue

Post by avenue33 » Sat Nov 29, 2014 2:42 pm

Any news?

The solution proposed by Jacob Christ at TWI I2C Wire Library Limited to 1 Byte in Slave Mode #310 helps a lot but isn't fully satisfactory as it requires changing the number of loops for each master.

This is a serious limitation for the PIC32 MCU. I'm afraid I'll have to consider another MCU from another manufacturer because of that specific missing feature.

Post Reply