PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. SOLVED!

General discussion on mikroC.
Post Reply
Author
Message
blips
Posts: 30
Joined: 11 Nov 2007 22:28

PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. SOLVED!

#1 Post by blips » 20 Jul 2010 15:11

Hi,

For the past three weeks I have been trying to solve this problem and I am at the end of my rope here. I have searched just about everywhere, on this forum and elsewhere for a solution which has gotten me this far. Hope someone can spot the error I am making.

First of all, I connect the SDA,SCL ports on both pics, and use 4k7 ohm pullups. (have tried 10k and 1k, same results).
The pics share a common ground of course.

The master is able to read data from the slave, YAY!
However, the slave is unable to receive data from the master. Can anyone here tell me why? I feel like I am so close..

My thoughts: Could it be the Internal Oscillator that is the problem? (if so, why does reading work?)


EDIT: When using Software I2C in the master, it gives the same results. So the problem must lie in the slave? It seems the slave only enters state 3 (Master Read, Last Byte Was An Adddress).

EDIT: Updated to MicroC Pro compiler, problem persists...


Cheers,
Blips

MASTER:

Code: Select all

/*
 * Test configuration:
     MCU:             P18F4550
     Dev.Board:       EasyPIC4
     Oscillator:      INT OSC, 08.0000 MHz
*/
unsigned cnt;
unsigned float second=388;
unsigned int test=0;
unsigned int test2=0;

const address = 0xC2;

void interrupt() {
  if (INTCON.TMR0IF) { // timer interrupt TMR0
    cnt++;                   // Increment value of cnt on every interrupt
    TMR0L  = 96;
    INTCON = 0x20;           // Set T0IE, clear T0IF
    if (cnt==second/2){
       PORTA.F2=~PORTA.F2;
       cnt=0;
    }
  }
}

void InitMain() {
  OSCCON = 0b01111010;
  TRISA = 0; LATA = 0;
  TRISB = 0; LATB = 0;
  TRISC = 0; LATC = 0;
  TRISD = 0; LATD = 0;
  TRISE = 0; LATE = 0;
  ADCON0 = 0; ADCON1 = 0x0F; ADCON2 = 0;  CMCON = 7;

  T0CON = 0xC4;      // Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 96;              // Timer0 initial value
  INTCON = 0xA0;           // Enable TMRO interrupt
  cnt = 0;                 // Initialize cnt

//Errata
  ADCON1 = 0b00001111;
  TRISB = 0;
  LATB = 0;
  TRISB = 0b00000011;
//
  Usart_Init(9600);
}

void send(unsigned short send_data, unsigned short slave){
  I2C_Init(100000);
  I2C_Start();
  I2C_Wr(slave);
  //I2C_Repeated_Start();
  I2C_Wr(send_data);
  I2C_Stop();
}

unsigned short read(unsigned short slave){
  unsigned short read_data=0;
  I2C_Init(100000);
  I2C_Start();
  I2C_Wr(slave+1);
  read_data = I2C_Rd(0);
  I2C_Stop();

  return read_data;
}

void main() {
  InitMain();
  while(1){

  // send 0xAA to slave
  send(0xAA,address);
  Delay_ms(1000);

  //read byte from slave 
  test = read(address);
  Usart_Write(test);
  Delay_ms(1000);
  }
}

SLAVE:

Code: Select all

/*
 * Test configuration:
     MCU:             P16F88
     Dev.Board:       EasyPIC4
     Oscillator:      INT OSC, 08.0000 MHz
     Ext. Modules:    -
     SW:              mikroC v 7.0.0.3
*/

unsigned long cnt=0;
unsigned long cnt2=0;
unsigned float second=388;
unsigned short test=0;

//------------------------------------------------------------------------------
const Addy = 0xC2;                    // set I2C device address
const Delay_Time = 250;               // port check delay
//------------------------------------------------------------------------------
//                      Global Processing Variables
//------------------------------------------------------------------------------
unsigned short j=0;                      // just dummy for buffer read
unsigned short rxbuffer=0;               //
unsigned short state=0;               //
unsigned short tx_data=0;                //
//------------------------------------------------------------------------------

void InitMain() {
  OSCCON = 0b01110000;   // INT clock used, set for 8 MHz.
  ANSEL =  0;
  PORTB =  0;
  TRISB =  0b00000000;
  TRISB =  0b00010011;
  TRISB = 255;
  PORTA = 0;
  TRISA =  0b00000000;
  OPTION_REG = 0b11000000;
  INTCON = 0b11010000;

  OPTION_REG = 0b11000100;
  TMR0  = 96;              // Timer0 initial value
  INTCON = 0b11110000;          // Enable TMRO interrupt
  
  
  ////
  ADCON1 = 0x0F;                          // All ports set to digital
  SSPADD =  Addy;                      // Get address (7bit). Lsb is read/write flag
  SSPCON = 0x36;                       // Set to I2C slave with 7-bit address
  PIE1.SSPIF = 1;                      // enable SSP interrupts

  PIE1.SSPIE = 1;                      // enable SSP interrupts

  ////
  
}

void interrupt() {

  if (INTCON.F1){      // RB0/INT
    INTCON.F1=0;       // Clear the interrupt flag
  }

  if (INTCON.TMR0IF) { // timer interrupt
    cnt++;                   // Increment value of cnt on every interrupt
    cnt2++;
    TMR0   = 96;
    INTCON = 0x20;           // Set T0IE, clear T0IF
    if (cnt2==second/2){
      cnt2=0;
     //PORTA.F7=~PORTA.F7;
    }
  }

  if (PIR1.SSPIF == 1){                // I2C Interrupt
    if (SSPCON.SSPOV==1){
      j=SSPBUF; // Read address to clear buffer
      SSPCON.SSPOV=0;
    }
    else {
      if (SSPSTAT.S==1 && SSPSTAT.R_W==0 && SSPSTAT.D_A==0 && SSPSTAT.BF==1){state=1;} // State 1: MASTER WRITE, LAST BYTE WAS AN ADDRESS
      if (SSPSTAT.S==1 && SSPSTAT.R_W==0 && SSPSTAT.D_A==1 && SSPSTAT.BF==1){state=2;} // State 2: MASTER WRITE, LAST BYTE WAS DATA
      if (SSPSTAT.S==1 && SSPSTAT.R_W==1 && SSPSTAT.D_A==0 && SSPSTAT.BF==0){state=3;} // State 3: MASTER READ, LAST BYTE WAS AN ADDRESS
      if (SSPSTAT.S==1 && SSPSTAT.R_W==1 && SSPSTAT.D_A==1 && SSPSTAT.BF==0){state=4;} // State 4: MASTER READ, LAST BYTE WAS DATA
      if (SSPSTAT.S==1 && SSPSTAT.D_A==1 && SSPSTAT.BF==0 && SSPSTAT.R_W==0 && SSPCON.CKP==1){state=5;} // State 5: MASTER NACK

      switch ( state ) {
        case 1:
          j=SSPBUF; // Read address to clear buffer
          if (SSPCON.SSPOV==1){ //overflow?
            SSPCON.SSPOV=0;
            j=SSPBUF; // Read address to clear buffer
          }
          break;

        case 2:
          rxbuffer=SSPBUF; // Read DATA from master.
          if (SSPCON.SSPOV==1){ //overflow?
            SSPCON.SSPOV=0;
            rxbuffer=SSPBUF; // Read address to clear buffer
          }
          break;

        case 3:
          SSPCON.CKP =0; // Hold the SCL line low
          tx_data++;
          SSPBUF = tx_data; // load buffer with data to be sent to master
          SSPCON.CKP =1; // Release the SCL line
          j=SSPBUF; // read to empty buffer
          break;

        case 4:
          SSPCON.CKP =0; // Hold the SCL line low
          SSPBUF = tx_data; // load buffer with data to be sent to master
          SSPCON.CKP =1; // Release the SCL line
          j=SSPBUF; // read to empty buffer
          break;

        case 5:
          break;

        default : break;
      }
       j=SSPBUF; //clear buffer
    }
    PIR1.SSPIF = 0;                    // reset SSP interrupt flag
  } //I2C interrupt end
}

void main() {
  InitMain();
  while(1)
  {
  }
}
Last edited by blips on 20 Jul 2010 21:23, edited 2 times in total.

blips
Posts: 30
Joined: 11 Nov 2007 22:28

Re: PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. so close!

#2 Post by blips » 20 Jul 2010 21:12

Found a solution finally,

The problem was with the master. I added a couple of delays..
void send(unsigned short send_data, unsigned short slave){
I2C_Init(100000);
I2C_Start();
I2C_Wr(slave);
Delay_ms(2); //2-20 sec <---- added delay
I2C_Wr(send_data);
Delay_ms(2); //2-20 sec <---- added delay
I2C_Stop();
}
"The notorious 18F4550" = 0
blips = 1

Anyhow, I'm not sure that this solution is entirely the best one. Mostly because I don't understand it fully. It would seem that the slave was having trouble keeping up with the master. I tried implementing the clock stretch on the slave like when it is in transmit mode (state 3 & 4). But that did'nt work. I think that would also interfere with other devices on the line.

Could someone here explain this?

The delays seem to do the trick, I will go further with the code and then post a howto here.. meanwhile, the code above will get people far.


also.. This topic seems to be a very popular one, should'nt there be a wiki about these kinds of topics. The forum gets you only so far.. Anyway just an idea </rant> ;)

blips
Posts: 30
Joined: 11 Nov 2007 22:28

Re: PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. so close!

#3 Post by blips » 20 Jul 2010 21:20

double post...
Last edited by blips on 20 Jul 2010 21:22, edited 1 time in total.

blips
Posts: 30
Joined: 11 Nov 2007 22:28

Re: PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. so close!

#4 Post by blips » 20 Jul 2010 21:21

Updated slave:

Code: Select all

/*
     MCU:             P16F88
     Dev.Board:       EasyPIC4
     Oscillator:      INT OSC, 08.0000 MHz
*/

unsigned long cnt=0;
unsigned long cnt2=0;
unsigned float second=388;
unsigned short test=0;

//------------------------------------------------------------------------------
const Addy = 0xC2;                    // set I2C device address
const Delay_Time = 250;               // port check delay
//------------------------------------------------------------------------------
//                      Global Processing Variables
//------------------------------------------------------------------------------
unsigned short j=0;                      // just dummy for buffer read
unsigned short rxbuffer=0;               //
unsigned short state=0;               //
unsigned short tx_data=0xD3;                //
//------------------------------------------------------------------------------


void InitMain() {
  OSCCON = 0b01110000;   // INT clock used, set for 8 MHz.
  ANSEL =  0;
  PORTB =  0;
  TRISB =  0b00000000;
  TRISB =  0b00010011;
  TRISB = 255;
  PORTA = 0;
  TRISA =  0b00000000;
  OPTION_REG = 0b11000000;
  INTCON = 0b11010000;

  OPTION_REG = 0b11000100;
  TMR0  = 96;              // Timer0 initial value
  INTCON = 0b11110000;          // Enable TMRO interrupt


  ////
  ADCON1 = 0x0F;                          // All ports set to digital
  SSPADD =  Addy;                      // Get address (7bit). Lsb is read/write flag
  SSPCON = 0x36;                       // Set to I2C slave with 7-bit address
  PIE1.SSPIF = 0;                      // enable SSP interrupts
  PIE1.SSPIE = 1;                      // enable SSP interrupts
  ////

}

void interrupt() {

  if (INTCON.F1){      // RB0/INT
    INTCON.F1=0;       // Clear the interrupt flag
  }

  if (INTCON.TMR0IF) { // timer interrupt
    cnt++;                   // Increment value of cnt on every interrupt
    cnt2++;
    TMR0   = 96;
    INTCON = 0x20;           // Set T0IE, clear T0IF
    if (cnt2==second/2){
      cnt2=0;
     //PORTA.F7=~PORTA.F7;
    }
     PORTA =rxbuffer;
  }

  if (PIR1.SSPIF == 1){                // I2C Interrupt
//          PORTA.F2=~PORTA.F2;
    state=0;                           //reset state
    PIR1.SSPIF = 0;                    // reset SSP interrupt flag
    if (SSPCON.SSPOV==1){
      SSPCON.SSPOV=0;
      j=SSPBUF; // Read address to clear buffer
    }
    else {
      if (SSPSTAT.S==1 && SSPSTAT.R_W==0 && SSPSTAT.D_A==0 && SSPSTAT.BF==1){state=1;} // State 1: MASTER WRITE, LAST BYTE WAS AN ADDRESS
      if (SSPSTAT.S==1 && SSPSTAT.R_W==0 && SSPSTAT.D_A==1 && SSPSTAT.BF==1){state=2;} // State 2: MASTER WRITE, LAST BYTE WAS DATA
      if (SSPSTAT.S==1 && SSPSTAT.R_W==1 && SSPSTAT.D_A==0 && SSPSTAT.BF==0){state=3;} // State 3: MASTER READ, LAST BYTE WAS AN ADDRESS
      if (SSPSTAT.S==1 && SSPSTAT.R_W==1 && SSPSTAT.D_A==1 && SSPSTAT.BF==0){state=4;} // State 4: MASTER READ, LAST BYTE WAS DATA
      if (SSPSTAT.S==1 && SSPSTAT.D_A==1 && SSPSTAT.BF==0 && SSPSTAT.R_W==0 && SSPCON.CKP==1){state=5;} // State 5: MASTER NACK

      switch ( state ) {
        case 0:
          j=SSPBUF;
          break;

        case 1:
          j=SSPBUF; // Read address to clear buffer
          break;

        case 2:
          rxbuffer=SSPBUF; // Read DATA from master.
          break;

        case 3:
          SSPCON.CKP =0; // Hold the SCL line low
//          SSPBUF = tx_data; // load buffer with data to be sent to master
          SSPBUF = rxbuffer; // load buffer with data to be sent to master
          SSPCON.CKP =1; // Release the SCL line
          j=SSPBUF; // read to empty buffer
          break;

        case 4:
          SSPCON.CKP =0; // Hold the SCL line low
          SSPBUF = tx_data; // load buffer with data to be sent to master
          SSPCON.CKP =1; // Release the SCL line
          j=SSPBUF; // read to empty buffer
          break;

        case 5:
          j=SSPBUF; // read to empty buffer
          break;

        default :
          j=SSPBUF; // read to empty buffer
          break;
      }
    }
    if (SSPCON.SSPOV==1){
      SSPCON.SSPOV=0;
      j=SSPBUF; // Read address to clear buffer
    }
  } //I2C interrupt end
}

void main() {
  InitMain();
  while(1)
  {
  }
}

CowBeast
Posts: 4
Joined: 08 Dec 2007 15:51

Re: PIC 2 PIC I2C - Master: 18F4550 Slave: 16F88.. SOLVED!

#5 Post by CowBeast » 16 Oct 2010 13:44

Does this code from the slave send an ACK back to the master?

I wanne use this code for my PIC16F877A slave.
He only needs to accept the recieved data. And send ACK.

Post Reply

Return to “mikroC General”