Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

[PIC16F1513][XC8/MPLAB X] - I2C Slave problem.

Status
Not open for further replies.

Urgon

New Member
AVE...

I'm working on my first big project using XC8 compiler. This is also first time I'm using any C dialect.
I'm trying to write code for device that will work as I2C slave. Because I'm trying to do this on PIC16F1513, XC8 has no ready to use peripherial libraries (for some reason). So I tried to work something out using datasheet for that microcontroller. I tested this code by using ChipKIT Max32 with network shield and UECIDE programming environment. It doesn't work however, and I have no idea, why. I'm trying to read output buffer from my slave using this code for ChipKIT board:
Code:
#include <Wire.h>
long i = 0;
void setup() {
  Wire.begin();        
  Serial0.begin(115200);
}

void loop() {
  Serial0.println(i);
  Wire.requestFrom(0xE1, 8);
  while (Wire.available()) {
    char c = Wire.read(); 
    Serial0.print(c, BIN); 
    Serial0.print(' ');
  }
  Serial0.println();
  i++;
  delay(1000);
}
Below are parts of code I wrote for PIC16F1513 with MPLAB X and XC8.
I2C related variables declaration:
C:
//Output buffer
volatile unsigned char outbuff[8];
//Input buffer
volatile struct {
    unsigned char command :8;
    unsigned char data0 :8;
    unsigned char data1:8;
    unsigned char data2:8;
}inbuff;
const unsigned char I2CADDR = 0xE1; //I2C address
Command resolve routine:
C:
void CommandResolve(){
    switch (inbuff.command){
        case 0xA0 : R[0] = inbuff.data0; G[0] = inbuff.data1; B[0] = inbuff.data2; state = 1; break;
        case 0xA1 : R[1] = inbuff.data0; G[1] = inbuff.data1; B[1] = inbuff.data2; state = 1; break;
        case 0xA2 : R[2] = inbuff.data0; G[2] = inbuff.data1; B[2] = inbuff.data2; state = 1; break;
        case 0xA3 : R[3] = inbuff.data0; G[3] = inbuff.data1; B[3] = inbuff.data2; state = 1; break;
        case 0xA4 : R[4] = inbuff.data0; G[4] = inbuff.data1; B[4] = inbuff.data2; state = 1; break;
        case 0xA5 : R[5] = inbuff.data0; G[5] = inbuff.data1; B[5] = inbuff.data2; state = 1; break;
        case 0xA6 : R[6] = inbuff.data0; G[6] = inbuff.data1; B[6] = inbuff.data2; state = 1; break;
        case 0xA7 : R[7] = inbuff.data0; G[7] = inbuff.data1; B[7] = inbuff.data2; state = 1; break;
        case 0xAB : autocalenable(); break;
        case 0xDE : testenable(); break;
        case 0xDD: state = 0; break;
        case 0xB0 : outbuff[0] = capar[0].caparst; outbuff[1] = capar[1].caparst; outbuff[2] = capar[2].caparst; outbuff[3] = capar[3].caparst; 
        outbuff[4] = capar[4].caparst; outbuff[5] = capar[5].caparst; outbuff[6] = capar[6].caparst; outbuff[7] = capar[7].caparst; break;
        case 0xB1 : outbuff[0] = capar[8].caparst; outbuff[1] = capar[9].caparst; outbuff[2] = capar[10].caparst; outbuff[3] = capar[11].caparst; 
        outbuff[4] = capar[12].caparst; outbuff[5] = capar[13].caparst; outbuff[1] = capar[14].caparst; outbuff[7] = capar[15].caparst; break;
        case 0xB2 : outbuff[0] = capar[16].caparst; outbuff[1] = capar[17].caparst; outbuff[2] = capar[18].caparst; outbuff[3] = capar[19].caparst; 
        outbuff[4] = capar[20].caparst; outbuff[5] = capar[21].caparst; outbuff[6] = capar[22].caparst; outbuff[7] = capar[23].caparst; break;
        case 0xB3 : outbuff[0] = capar[24].caparst; outbuff[1] = capar[25].caparst; outbuff[2] = capar[26].caparst; outbuff[3] = capar[27].caparst; 
        outbuff[4] = capar[28].caparst; outbuff[5] = capar[29].caparst; outbuff[6] = capar[30].caparst; outbuff[7] = capar[31].caparst; break;
    }            
}
Main routine:
C:
void main(int argc, char** argv) {
    //Clock init
    OSCCON = 0x78;
    OSCSTAT = 0x00;
    //I/O Init
    TRISA = 0;
    TRISB = 0x2F;
    TRISC = 0xF8;
    ANSELB = 0x2F; 
    ANSELC = 0xE0;
    //Interrupts
    INTCONbits.PEIE = 1;
    PIE1bits.SSPIE = 1;
    //MSSP init
    SSPCON1bits.SSPM = 0x6; //I2C Slave mode
    SSPCON2bits.GCEN = 1; //General Call Interrupt
    SSPCON2bits.SEN = 1;
    SSPCON3bits.SDAHT = 1; //300ns SDA hold time for longer cables 
    SSPCON3bits.SCIE = 1; //Start condition enable bit     
    SSPADD = I2CADDR;
    //Enables
    SSPCON1bits.SSPEN = 1;
    INTCONbits.GIE = 1;
    while(1)
    {
        // State list
        // 0 = CVD scan
        // 1 = RGB scan
        // 2 = Command resolve
        // 3 = Autocalibration
        // 4 = Test Routine
        if (state>4) state = 0;
        switch (state){
        case 0: CVDScan; state = 1; break;
        case 1: RGBScan; state = 0; break;
        case 2: CommandResolve(); break;
        case 3: autocal(); state = 0; break;
        case 4: test(); state = 4; break;
        }
    }
}
Interrupt routine:
C:
void interrupt myint()
{
    unsigned char i;
    if (SSPIE && SSPIF)
    {
        if (!SSPSTATbits.R_nW)
        {
            state = 2;
            SSPIF = 0;
            if (!SSPSTATbits.D_nA)
                i = SSPBUF;
            else
                inbuff.command = SSPBUF;
            if (SSPSTATbits.P) {
                i = 0;
                return;
                }
            CKP = 1;
            while (!SSPIF);
            SSPIF = 0;
            inbuff.data0 = SSPBUF;
            if (SSPSTATbits.P) {
                i = 0;
                return;
                }
            CKP = 1;
            while (!SSPIF);
            SSPIF = 0;
            inbuff.data1 = SSPBUF;
            if (SSPSTATbits.P) {
                i = 0;
                return;
                }
            CKP = 1;
            while (!SSPIF);
            SSPIF = 0;
            inbuff.data2 = SSPBUF;
            if (SSPSTATbits.P) {
                i = 0;
                return;
                }
        }
        else
        {
            SSPIF = 0;
            if (!SSPSTATbits.D_nA)
                i = SSPBUF;
            SSPSTATbits.R_nW = 1;
            for (i = 0; i <8; i++)
            {
                SSPBUF = outbuff[i];
                CKP = 1;
                while (!SSPIF);
                SSPIF = 0;
                if (!ACKSTAT)
                {
                    SSPIF = 0;
                    return;
                } 
            }
        }          
    }
}
 
At first glance , you seem to be going in right direction, with a state machine , I had 16F1847 as slave but in assembler, was difficult, sw I2C slave has lots of problems to solve, I will take a longer look and reply.
Edit ... just spotted
The slave address E1 will be shifted by Wire << 1 to include R/W bit so becomes C2 or C3 the address byte sent ,
 
Last edited:
I'm having similar problems with I2C on my pic32mx440.... It seems like the PBclk isn't right... There is an errata somewhere but I can't find it... The peripheral clock cannot be 80000000 ( or it would seem ) I have tried several PBclk divisions but the I2C really is giving me a headache at the moment!!! I will update you as I find things out!!!
 
AVE...
granddad
On the slave side address is programmed into hardware and hardware does both shifting and address matching, and also determining if it is a read or a write command from master. That's why I'm testing SSPSTATbits.R_nW bit.
On the master side everything is supposed to be done correctly by wire library, which was copied from Arduino library, and modified to work with ChipKIT. Unfortunately, it is a bit lacking, because I can access only one I2C module, while ChipKIT MAX32 has two, and network shield adds pullups to both of them. And I'd rather use second I2C module, because it uses current mirrors instead of resistors, which is better for longer cables. And my cable is only 60cm long.
 
Urgon ... Believe me 'wire' will send 7 bits with r/w shifted in to bit 0 , have done it with ChipKit UNO32. I2C to PIC .
Q are you getting an I2C interrupt in the slave , If address matches you will . as long as the hardware is set up correctly.
 
Urgon. snippit of my ChipKit UNO32 master code with I2C to 24LC256 EEprom , MCP7940 , and PIC16F1939 running LCD, Thermal printer, and keypad...

Code:
#include <Wire.h>
const byte EEprom = 0x50 ;   // 24LC256 shifted to A0
const byte KDP1939 = 0x22;   // PIC16F1939,shifted to 44,  LCD DISPLAY, 4X4 KEYPAD AND THERMAL PRINTER
const byte MCP7940 = 0x6F;   // Shifted to 0xDE

////////////////////////////////////////////////////////////
void enqKDP(){   // enquire thermal printer status (05)> (06) ack ready
  Wire.beginTransmission(KDP1939); // transmit INQ to PRINTER
  Wire.send(0xF7); // Slave COMMAND
  Wire.send(0x05);  //   ENQ
  Wire.endTransmission();    // stop transmitting
  delay(1);  // required for PIC to service interrupt.
  Wire.requestFrom(KDP1939,1);  //
  if(!Wire.available()){
  }    // needs timer no responce
  KDPstat = Wire.receive();
  Wire.endTransmission();
}
 
AVE...

Today I've changed few things in the both programs.
ChipKIT side:
Code:
#include <Wire.h>
long i = 0;
void setup() {
  Wire.begin();        
  Serial0.begin(115200);
}

void loop() {
  Serial0.println(i);
  Wire.requestFrom(0x55, 8);
  while (Wire.available()) {
    char c = Wire.read(); 
    Serial0.print(c, BIN); 
    Serial0.print(' ');
  }
  Serial0.println();
  i++;
  delay(1000);
}
PIC16F1513 side:
C:
volatile struct {
    unsigned char command :8;
    unsigned char data0 :8;
    unsigned char data1:8;
    unsigned char data2:8;
}inbuff;
const unsigned char I2CADDR = 0xAA; //I2C address
(...)
void CommandResolve(){
  switch (inbuff.command){
  case 0xA0 : R[0] = inbuff.data0; G[0] = inbuff.data1; B[0] = inbuff.data2; state = 1; break;
  case 0xA1 : R[1] = inbuff.data0; G[1] = inbuff.data1; B[1] = inbuff.data2; state = 1; break;
  case 0xA2 : R[2] = inbuff.data0; G[2] = inbuff.data1; B[2] = inbuff.data2; state = 1; break;
  case 0xA3 : R[3] = inbuff.data0; G[3] = inbuff.data1; B[3] = inbuff.data2; state = 1; break;
  case 0xA4 : R[4] = inbuff.data0; G[4] = inbuff.data1; B[4] = inbuff.data2; state = 1; break;
  case 0xA5 : R[5] = inbuff.data0; G[5] = inbuff.data1; B[5] = inbuff.data2; state = 1; break;
  case 0xA6 : R[6] = inbuff.data0; G[6] = inbuff.data1; B[6] = inbuff.data2; state = 1; break;
  case 0xA7 : R[7] = inbuff.data0; G[7] = inbuff.data1; B[7] = inbuff.data2; state = 1; break;
  case 0xAB : autocalenable(); break;
  case 0xDE : testenable(); break;
  case 0xDD: state = 0; break;
  case 0xB0 : outbuff[0] = capar[0].caparst; outbuff[1] = capar[1].caparst; outbuff[2] = capar[2].caparst; outbuff[3] = capar[3].caparst;
  outbuff[4] = capar[4].caparst; outbuff[5] = capar[5].caparst; outbuff[6] = capar[6].caparst; outbuff[7] = capar[7].caparst; break;
  case 0xB1 : outbuff[0] = capar[8].caparst; outbuff[1] = capar[9].caparst; outbuff[2] = capar[10].caparst; outbuff[3] = capar[11].caparst;
  outbuff[4] = capar[12].caparst; outbuff[5] = capar[13].caparst; outbuff[1] = capar[14].caparst; outbuff[7] = capar[15].caparst; break;
  case 0xB2 : outbuff[0] = capar[16].caparst; outbuff[1] = capar[17].caparst; outbuff[2] = capar[18].caparst; outbuff[3] = capar[19].caparst;
  outbuff[4] = capar[20].caparst; outbuff[5] = capar[21].caparst; outbuff[6] = capar[22].caparst; outbuff[7] = capar[23].caparst; break;
  case 0xB3 : outbuff[0] = capar[24].caparst; outbuff[1] = capar[25].caparst; outbuff[2] = capar[26].caparst; outbuff[3] = capar[27].caparst;
  outbuff[4] = capar[28].caparst; outbuff[5] = capar[29].caparst; outbuff[6] = capar[30].caparst; outbuff[7] = capar[31].caparst; break;
  }
  inbuff.command = 0;
}
Amazingly, it worked. For few seconds, because there were problems with cable. I've got some response, few times, and even something I expected. So later I'll write some code for my other board with PIC18F45K50 to use it as USB<>I2C bridge and then I'll report back.
 
Urgon... Can you not develop your code on a short cable <20cm . make life easier . PS what is AVE...
 
AVE*...

I tried just that, with some Dupond jumper cables, but it wasn't really better. I checked all connections on my prototype board, so now I'm suspecting problems with connections on ChipKIT, which I bought used. But because I have a proto board with PIC18F45K50 with working USB code on it, I'll just solder a socket for my prototype board and write some I2C routines.

* - Hail in latin, as in Ave, Cæsar, morituri te salutant - Hail, Caesar, those who are about to die salute you.
 
AVE...

After some fiddling with PIC18F45K50 I converted it into Pinguino board and wrote this code:
Code:
u8 I2C_address = 0xAA; //slave address

void setup() {
   CDC.printf("Test");
   CDC.getKey();
  I2C.init(I2C_MASTER_MODE, I2C_100KHZ);  
  delay(100);
}
void loop() {
u8 c[8]; 
u8 i = 0;
   I2C.start();    
   if(!I2C.write(I2C_address + 1))
   CDC.printf("Nie ma takiego numeru! \r\n");
   else {
      I2C.wait();
      CDC.printf("\r\n>");
      while(i < 8){
          c[i] = I2C.read();;
          I2C.sendAck();
          i++;
      }
      I2C.sendNack();
      I2C.stop();
      i = 0;
      while(i<8){
          CDC.printf(" 0x%X", c[i]);
          i++;
      }
      i = 0;
  }
  delay(1000);
}
Then I ran RealTerm and after receiving some messages I connected it to my project ending with this log:
Code:
Nie ma takiego numeru! 
                                                       
Nie ma takiego numeru! 
                                                       
Nie ma takiego numeru! 
                                                       
Nie ma takiego numeru! 
                                                       
Nie ma takiego numeru! 
                                                       
                         
                                                     
> 0x56 0xAC 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
                                     
>
After first read it stops working. I don't know, why. The numbers are correct because I didn't calibrate the project. Any suggestions?
 
Hi Urgon anything ending in uino is 'out of range' in my world :) , data not completing requests would suggest missing master acknowledge . To "mess" with I2C you really need a bus sniffer , I got a ' bus pirate' on line , not expensive, although I have not used it yet ...
 
AVE...

I finally got a logic analyzer, I also discovered that when I disconnect and reconnect Pinguino to the device board, I get data out. So I sniffed data from I2C while device was connected, and then disconnected and connected multiple times. Device number is 0x71. Below I attached the raw logic data for Saleae Logic and text log of decoded communication (BTW, trying to attach file with unsupported .logicdata extension caused an error that crashed Firefox).
 

Attachments

  • i2c-1.zip
    23.9 KB · Views: 241
AVE...

I obtained MCP2221 USB<>I2C bridge, and after some fiddling and changing my code I've got it almost working. I used source code for slave device ver. 2 from App Note
AN734. I changed variable names to fit rest of my firmware. My ISR looks like this:
C:
void interrupt myint()
{
  if(SSPIF)  // check to see if SSP interrupt
  {
  if(SSPSTATbits.R_nW)  // Master read (R_nW = 1)
  {
  if(!SSPSTATbits.D_nA)  // last byte was an address (D_nA = 0)
  {
  junk = SSPBUF;  // dummy read to clear BF bit
  index_i2c = 0;  // clear index pointer
  if(index_i2c < TX_ELMNTS)
  {  // Does data exceed number of allocated bytes?
  SSPBUF = outbuff[index_i2c];  // load SSPBUF with data
  outbuff[index_i2c++] = clear;  // clear that location
  }  // and increment index
  else  // trying to exceed array size
  {
  junk = SSPBUF;  // dummy read to clear BF bit
  }
  SSPCON1bits.CKP = 1;  // release CLK
  }
  if(SSPSTATbits.D_nA)  // last byte was data
  {
  if (index_i2c < TX_ELMNTS)
  {  // Does data exceed number of allocated bytes?
  SSPBUF = outbuff[index_i2c];  // load SSPBUF with data
  outbuff[index_i2c++] = clear;  // clear that location
  }  // and increment index
  else
  {
  junk = SSPBUF;  // dummy read to clear BF bit
  }
  SSPCON1bits.CKP = 1;  // release CLK
  }
  }
   
  if(!SSPSTATbits.R_nW)       // master write (R_nW = 0)
  {
  if(!SSPSTATbits.D_nA)  // last byte was an address (D_nA = 0)
  {
  junk = SSPBUF;       // read buffer to clear BF
         SSPCON1bits.CKP = 1;  // release CLK
  }
  if(SSPSTATbits.D_nA)  // last byte was data (D_nA = 1)
  {
     if (index_i2c < RX_ELMNTS)   
  {  // Does data exceed number of allocated bytes?
  switch(index_i2c){
  case 0: inbuff.command = SSPBUF; break;
  case 1: inbuff.data0 = SSPBUF; break;
  case 2: inbuff.data1 = SSPBUF; break;
  case 3: inbuff.data2 = SSPBUF; break;
  }
  index_i2c++;
  }  // load array with data
  else
  {
  index_i2c = 0;  // reset the index pointer
  }
         if(SSPCON1bits.WCOL)     // Did a write collision occur?
         {
  SSPCON1bits.WCOL = 0;  // clear WCOL bit
  junk = SSPBUF;  // clear SSPBUF
         }
         SSPCON1bits.CKP = 1;       // release CLK
  }
     }
  }
  if(BCLIF)  // Did a bus collision occur?
  {
  junk = SSPBUF;  // clear SSPBUF
     BCLIF = 0;  // clear BCLIF
     SSPCON1bits.CKP = 1;  // Release CLK
  }
  SSPIF = 0;  // clear SSPIF
}
However when using MPC2221 I2C utility I get this:
Code:
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
Read error. Error code: I2C_READ002 -25
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
OK
< 00 00 00 00 00 00 00 00 
> I2C Write, Address = 38, Data: B0 Delay = 0
Timeout. Error code: -18
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
OK
< 00 00 00 00 00 00 00 00 
> I2C Write, Address = 38, Data: B0 Delay = 0
Timeout. Error code: -18
> I2C Write, Address = 38, Data: B0 Delay = 0
OK
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
Read error. Error code: I2C_READ002 -25
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
OK
< 00 00 00 00 00 00 00 00 
> I2C Write, Address = 38, Data: B0 Delay = 0
Timeout. Error code: -18
> I2C Write, Address = 38, Data: B0 Delay = 0
OK
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
Read error. Error code: I2C_READ002 -25
> I2C Read, Address = 38, Read 8 bytes, Delay = 0
OK
< 00 00 00 00 00 00 00 00
Any suggestions?
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top