1. 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.
    Dismiss Notice

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

Discussion in 'Microcontrollers' started by Urgon, Nov 12, 2015.

  1. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    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 (text):
    #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:
    Code (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:
    Code (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:
    Code (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:
    Code (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;
                    }
                }
            }          
        }
    }
     
  2. granddad

    granddad Active Member

    Joined:
    Jan 18, 2015
    Messages:
    749
    Likes:
    75
    Location:
    Worcestershire UK
    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: Nov 13, 2015
  3. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,148
    Likes:
    907
    Location:
    Rochdale UK
    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!!!
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0

    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.
     
  6. granddad

    granddad Active Member

    Joined:
    Jan 18, 2015
    Messages:
    749
    Likes:
    75
    Location:
    Worcestershire UK
    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.
     
  7. granddad

    granddad Active Member

    Joined:
    Jan 18, 2015
    Messages:
    749
    Likes:
    75
    Location:
    Worcestershire UK
    Urgon. snippit of my ChipKit UNO32 master code with I2C to 24LC256 EEprom , MCP7940 , and PIC16F1939 running LCD, Thermal printer, and keypad...

    Code (text):

    #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();
    }
     
  8. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    AVE...

    Today I've changed few things in the both programs.
    ChipKIT side:
    Code (C++):
    #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:
    Code (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.
     
  9. granddad

    granddad Active Member

    Joined:
    Jan 18, 2015
    Messages:
    749
    Likes:
    75
    Location:
    Worcestershire UK
    Urgon... Can you not develop your code on a short cable <20cm . make life easier . PS what is AVE...
     
  10. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    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.
     
  11. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    AVE...

    After some fiddling with PIC18F45K50 I converted it into Pinguino board and wrote this code:
    Code (text):
    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 (text):
    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?
     
  12. granddad

    granddad Active Member

    Joined:
    Jan 18, 2015
    Messages:
    749
    Likes:
    75
    Location:
    Worcestershire UK
    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 ...
     
  13. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    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).
     

    Attached Files:

  14. Urgon

    Urgon New Member

    Joined:
    Sep 11, 2012
    Messages:
    13
    Likes:
    0
    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:
    Code (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 (text):
    > 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?
     

Share This Page