Author Topic: iic (i2c) communication between 2 processors  (Read 12553 times)

Offline kaflick

  • Newbie
  • *
  • Posts: 20
    • View Profile
iic (i2c) communication between 2 processors
« on: August 12, 2011, 09:54:21 PM »
We are trying to use i2c to communicate between two processors.

Is there anyone who is doing anything like this?

We have two processor boards with an i2c link between them and
it is the one thing that is not working for us yet that is holding us up.

Here is my init code:


//
//   Init I2C
//
   switch (BoardNum) {
      case FRONTBOARD:
          tIICParameters.Channel = FRONT_IIC_CHANNEL;  // channel 0
         break;
      case BACKBOARD:
          tIICParameters.Channel = BACK_IIC_CHANNEL;  // also channel 0
         break;
   }
    tIICParameters.usSpeed = 400;                                        // 1M
    tIICParameters.Rx_tx_sizes.TxQueueSize = 64;                         // transmit queue size
    tIICParameters.Rx_tx_sizes.RxQueueSize = 64;                         // receive queue size
    tIICParameters.Task_to_wake = 0;                                     // no wake on transmission
    if ((IICPortID = fnOpen( TYPE_IIC, FOR_I_O, &tIICParameters)) !=0) {
      i=sprintf(DebugStr,"I2C Init %d\n\r",IICPortID);
      fnWrite(SerialPortID, (unsigned char *)DebugStr,i);
    }// open the channel with defined configurations
    else {
      fnWrite(SerialPortID, (unsigned char *)"I2C Init Error\n\r",16);
    }

It opens with IICPortID 17 on both ends and when I try to send or receive the system hangs.
I have on system that tries to send:

unsigned char teststr[]={
   0,'A',0
};

fnWrite(IICPortID, teststr,2);

The other system reads:

tmpcnt=fnRead(IICPortID,(unsigned char *)i2cbuf,2);

I don't understand the i2c hardware/software enough to know if I am doing it right.
Any suggestions?

Keith Flick
kaflick@sev.org


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iic (i2c) communication between 2 processors
« Reply #1 on: August 12, 2011, 10:29:59 PM »
Hi Keith

The I2C driver is a master mode driver. That means that it can write and read slave devices but can't act as a slave to an I2C bus.

If you have two processors connected together on a single I2C bus and you want to send data from one to the other via I2C, the receiving one needs to operate as an I2C slave. This is not possible with the master-only driver.

If two masters are on the same I2C bus it shouldn't, however, cause the system to hang (unless you mean that the I2C bus doesn't work) but they won't be able to exchange data.

Generally I wouldn't recommend exchanging data via I2C since it can get quite complicated (if there are more than one master at least - due to collisions). To communicate between two processors it is simpler using (for example) UART (if possible).

If the I2C connection is a necessity it will be necessary to add a slave mode to one end (assuming that one can always be master and the other always slave - the simplest case).

Regards

Mark





Offline kaflick

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: iic (i2c) communication between 2 processors
« Reply #2 on: August 13, 2011, 07:36:19 PM »
Is there any place to see a description of a slave mode driver for one end?

One can be a slave as far as I am concerned.

We already have the boards build and we just didn't understand i2c enough to begin with.

Keith Flick

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iic (i2c) communication between 2 processors
« Reply #3 on: August 13, 2011, 10:51:16 PM »
Keith

The I2C controller description in the processor's user's manual contains quite a lot of information about the I2C operation, including some examples.
Also visit the NXP web size (they invented the I2C bus) because they also have a lot of application notes about it.

Basically, a slave device doesn't actively send data. It waits for data to be collected from it (similar to SPI slave) or received data whenever it arrives.
A master can send data to slaves or read data from slaves (addressing individual ones) whenever it wants to.

The complication with multiple masters is that they may try to do something at the same time, resulting in a collision. This can be detected and one of the masters can then abort its operation and try again later. If there is a fixed master and a fixed slave there can not be any collisions, which makes things easier.

Regards

Mark

Offline kaflick

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: iic (i2c) communication between 2 processors
« Reply #4 on: September 01, 2011, 01:38:55 PM »
I have set one processor up a a slave using the following code (from an example in the 52259 manual):


//
// I2C slave routines
//

static __interrupt__ void _IIC_InterruptSlave(void) // I2C0 interrupt
{
   char junk;
   
    I2SR&=~IIC_IIF;     // clear the interrupt flag
    if(I2SR&IIC_IAL) {
       I2SR&=~IIC_IAL;   //   If IAL (arbitration lost) set, clear it
       if(I2SR&IIC_IAAS) {
          buffOffset=1;    // Ignore space for the address
       } else return;
    }
    if(I2SR&IIC_IAAS) { // IAAS is set, received our slave address
       ParamBackBoard=1;
       BackTimer=1000;
       if(I2SR&IIC_SRW) { //   if SRW set, tx mode
         I2CR|=IIC_MTX;   //   Set hardware to tx mode
          buffOffset=1;   //   Init buffer address
          DEBUG_LED_ON;   //   KAF
          I2DR=output_i2c.data.buf[buffOffset++];   //   Send the first character
       } else { //   rx mode   
         I2CR&=~IIC_MTX;   //   Set hardware to rx mode
          buffOffset=1;   //   Init buffer address
          junk=I2DR;   // Do a dummy read
       }
          
    } else {   //   IAAS not set, in the middle of communication
       if(I2CR&IIC_MTX){ //   If in tx mode
          if(I2SR&IIC_RXACK){   //   Received an ACK so ready for next byte
              I2DR=output_i2c.data.buf[buffOffset++];   //   Send the next character
          }else {   // Done transmitting data
             I2CR&=~IIC_MTX;   //   Set hardware to rx mode
             junk=I2DR;   // Do a dummy read
            
          }
       }else {   //   If in rx mode
           input_i2c.data.buf[buffOffset++]=I2DR;   // Read the next character
         
       }
       
    }
}

//
// Configure the IIC hardware
//
void fnConfigIICSlave(IICTABLE *pars)
{
    unsigned char ucSpeed;
    POWER_UP(POWER_I2C);                                                 // enable clock to module {46}
    PASPAR |= (AS_I2C_SCL_0_FUNCTION | AS_I2C_SDA_0_FUNCTION);           // configure the SDA and SCL pins on AS

    // The calculation of the correct divider ratio doesn't follow a formular but is best taken from a table.
    // The required divider value is ((BUS_CLOCK/1000)/pars->usSpeed). Various typical speeds are supported here.
    switch (pars->usSpeed) {                                             // {31}
    case 400:                                                            // high speed IIC
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x32;                                                  // set about 400k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x0d;                                                  // set about 400k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x0b;                                                  // set about 400k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x0a;                                                  // set about 400k with 40MHz bus frequency
    #endif
        break;
    case 100:
    default:                                                             // default to 100kHz
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x3a;                                                  // set about 100k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x15;                                                  // set about 100k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x37;                                                  // set about 100k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x36;                                                  // set about 100k with 40MHz bus frequency
    #endif
        break;

    case 50:
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x3e;                                                  // set about 50k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x19;                                                  // set about 50k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x3b;                                                  // set about 50k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x3a;                                                  // set about 50k with 40MHz bus frequency
    #endif
        break;
    }
    I2FDR = ucSpeed;
    I2CR = (IIC_IEN);                                                   // enable IIC controller and set slave mode
    IC_ICR_0_17 = IIC_INTERRUPT_PRIORITY;                                // define interrupts level and priority
    fnSetIntHandler(IIC_VECTOR, (unsigned char *)_IIC_InterruptSlave);        // enter the handler routine
    IC_IMRL_0 &= ~(IIC_PIF_INT_L | MASK_ALL_INT);
    I2CR|=IIC_IIEN;      //   Enable interrupts


I write and read from the master with the following code:


static const unsigned char ucReadI2C[] = {17, 1, OWN_TASK};

      retval=fnWrite(IICPortID, (unsigned char *)&(output_i2c.zero), SIZEOFCOMSTRUCT+2);
      sprintf(buf,"W=%d\n\r",retval);
      fnDebugMsg(buf);
      retval=fnRead(IICPortID, (unsigned char *)ucReadI2C, 0);
      sprintf(buf,"R=%d\n\r",retval);
      fnDebugMsg(buf);

At the end of the main look I added the following code to input the read message data:

      //
      //   I2C read code
      //
       if (fnMsgs(IICPortID) != 0) {                                        // if IIC message waiting
          DEBUG_LED2_ON;   //KAF
         sprintf(DebugStr,"M=%d ",fnMsgs(IICPortID));
         fnDebugMsg(DebugStr);
           while ((Length = fnRead(IICPortID, ucInputMessage, MEDIUM_MESSAGE)) != 0) {
            sprintf(DebugStr,"L=%d\r\n",Length);
            fnDebugMsg(DebugStr);
            memcpy(&input_i2c.count,ucInputMessage,Length);
           }
       }


The writes work OK, if I comment out the reads it runs forever.

If I try to read I get this debug output:

Hello, world... Coldfire V2 MCU
BoardNum=1
I2C Init 15
W=18
R=3
W=1

In the middle of writing the second write string the system hangs.
The debug led never turns on (DEBUG_LED2 from the I2C read code) so
it never gets there.

Anyone see anything wrong?

Keith Flick
kaflick@sev.org


Offline kaflick

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: iic (i2c) communication between 2 processors
« Reply #5 on: September 01, 2011, 08:39:48 PM »
I have found the problem in the Interrupt driver in the slave.

One of the status bits had the opposite polarity of what I thought.

If anyone would like a working chunk of I2c Slave interrupt driver here it is:



static __interrupt__ void _IIC_InterruptSlave(void) // I2C0 interrupt
{
   char junk;
   
    I2SR&=~IIC_IIF;     // clear the interrupt flag
    if(I2SR&IIC_IAL) {
       I2SR&=~IIC_IAL;   //   If IAL (arbitration lost) set, clear it
       if(I2SR&IIC_IAAS) {
          buffOffset=0;
       } else return;
    }
    if(I2SR&IIC_IAAS) { // IAAS is set, received our slave address
       if(I2SR&IIC_SRW) { //   if SRW set, tx mode
      I2CR|=IIC_MTX;   //   Set hardware to tx mode
          buffOffset=0;   //   Init buffer address
          I2DR=buf[buffOffset++];   //   Send the first character
       } else { //   rx mode   
      I2CR&=~IIC_MTX;   //   Set hardware to rx mode
          buffOffset=0;   //   Init buffer address
          junk=I2DR;   // Do a dummy read
     }
          
    } else {   //   IAAS not set, in the middle of communication
       if(I2CR&IIC_MTX){ //   If in tx mode
          if(I2SR&IIC_RXACK){   //   Failed to receive an ACK?
               I2CR&=~IIC_MTX;   //   Set hardware to rx mode
             junk=I2DR;   // Do a dummy read
          }else {   // Not done transmitting data
              I2DR=buf[buffOffset++];   //   Send the next character
          }
       }else {   //   If in rx mode
           buf[buffOffset++]=I2DR;   // Read the next character
         
       }
       
    }
}

Here is the setup routine for it:

//
// Configure the IIC hardware
//
void fnConfigIICSlave(IICTABLE *pars)
{
    unsigned char ucSpeed;
    POWER_UP(POWER_I2C);                                                 // enable clock to module {46}
    PASPAR |= (AS_I2C_SCL_0_FUNCTION | AS_I2C_SDA_0_FUNCTION);           // configure the SDA and SCL pins on AS

    // The calculation of the correct divider ratio doesn't follow a formular but is best taken from a table.
    // The required divider value is ((BUS_CLOCK/1000)/pars->usSpeed). Various typical speeds are supported here.
    switch (pars->usSpeed) {                                             // {31}
    case 400:                                                            // high speed IIC
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x32;                                                  // set about 400k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x0d;                                                  // set about 400k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x0b;                                                  // set about 400k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x0a;                                                  // set about 400k with 40MHz bus frequency
    #endif
        break;
    case 100:
    default:                                                             // default to 100kHz
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x3a;                                                  // set about 100k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x15;                                                  // set about 100k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x37;                                                  // set about 100k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x36;                                                  // set about 100k with 40MHz bus frequency
    #endif
        break;

    case 50:
    #if BUS_CLOCK > 60000000                                             // 80MHz
        ucSpeed = 0x3e;                                                  // set about 50k with 80MHz bus frequency
    #elif BUS_CLOCK > 50000000                                           // 60MHz
        ucSpeed = 0x19;                                                  // set about 50k with 60MHz bus frequency
    #elif BUS_CLOCK > 40000000                                           // 50MHz
        ucSpeed = 0x3b;                                                  // set about 50k with 50MHz bus frequency
    #else                                                                // assume 40MHz
        ucSpeed = 0x3a;                                                  // set about 50k with 40MHz bus frequency
    #endif
        break;
    }
    I2FDR = ucSpeed;
    I2CR = (IIC_IEN);                                                   // enable IIC controller and set slave mode
    IC_ICR_0_17 = IIC_INTERRUPT_PRIORITY;                                // define interrupts level and priority
    fnSetIntHandler(IIC_VECTOR, (unsigned char *)_IIC_InterruptSlave);        // enter the handler routine
    IC_IMRL_0 &= ~(IIC_PIF_INT_L | MASK_ALL_INT);
    I2CR|=IIC_IIEN;      //   Enable interrupts
}

Feel free to use it.  You might want to put it in the i2c doc as an example.

The read routine in the application loop works to.  Here it is:

       if (fnMsgs(IICPortID) != 0) {                                        // if IIC message waiting
           while ((Length = fnRead(IICPortID, ucInputMessage, MESSAGESIZE)) != 0) {
            memcpy(&input_i2c.count,ucInputMessage,Length);
           }
       }

Keith Flick

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iic (i2c) communication between 2 processors
« Reply #6 on: September 01, 2011, 09:36:28 PM »
Hi Keith

Many thanks for sharing your code for the slave operation, I am sure that it will come in handy ;-)

Regards

Mark