Slave I2C interrupt latency?

Discussion in 'Embedded Systems and Microcontrollers' started by dtrott, Sep 30, 2011.

  1. dtrott

    Thread Starter New Member

    Sep 29, 2011
    2
    0
    Hi all,
    Here is an unusual one, maybe someone brighter than me can help.
    I have a pic18f4550 as a USB device and a master to a pic16f88 acting as a slave that will be doing analogue sampling. At the moment, all the master is doing is repeating a write of one bight. After many hours not having luck trying to debug both, I hooked the master up to an eeprom just to get that one sorted from an I2C standpoint. No problems, too easy.
    I did find that I hadn't set my baud rate correctly, it was at about 225kHz but now fixed that and its at 100kHz (verified with scope) and pins tied high with 2k2 resistors.
    After i fixed that, the slave worked - kind of.
    I have the master turning on a green light when an ack is received and a red light when a nack is received (easier to see than getting the scope triggering properly). With the master running by itself and the slave in debug with an ICD2 through MPLAB, I couldn't get any acks when running the program right after a chip burn. However, after a reset, I could, but then only acks for a second maybe (and some times more, sometime less).
    So after some more hours of debugging I thought I had it. It was acking continuously. So I thought I'd clean up my code and get rid of all the temporary variables that I was using to find faults.
    This is where it got strange. After I deleted one temporary variable in the isr (which did a second bitmask on the SSPSTATUS register, checking for a stop bit ) I could only get an ack for a second again. In the code below you'll see in the interrupt routine that there are 4 NOP instructions. One more than that, < 1 second of acking, one less than that, < 1 second of acking, 4 - continuous acking.
    Anyone with ideas? Why is 4 nop's the magic number?
    By the way, using Hi-Tech for the slave and C18 for the master.

    Slave code:

    Code ( (Unknown Language)):
    1.  
    2.  
    3. #include <htc.h>
    4. #define true 1
    5. #define BYTES 64
    6. /***** Global Variable ********/
    7. void delay(int);
    8. void init(void);
    9. void i2c_init(void);
    10. void i2c_start(void);
    11. void i2c_write(char);
    12. void i2c_stop(void);
    13. void i2c_wait(void);
    14.  
    15. void i2c_init(void)
    16. {  
    17.  SSPEN      = 1;   //Enables Serial Port Mode    
    18.  INTCONbits.PEIE = 1;      
    19.  SSPCONbits.CKP =1; //Clock Released  
    20.  SSPBUF = 0 ;
    21.  
    22.  /* SSPCON REGISTERS */
    23.  SSPCONbits.SSPM = 0x6;
    24.  
    25.  /* SSPSTAT REGISTERS */
    26.  TRISB4 = 1;  // SCL as input
    27.  SMP = 0;   //Slew Rate Control Disabled
    28.  CKE   = 0;   //SMBus Specific Inputs Disabled
    29.  SSPADD = 0xAE;
    30. }
    31. void delay(int pause)
    32. {
    33.    unsigned int i, j, k;
    34.  
    35.    unsigned int clock = 6;
    36.    unsigned int cycle = 14;
    37.  
    38.    for(i = 0; i <= pause; i++){
    39.    for(j = 0; j <= clock; j++){
    40.    for(k = 0; k <= cycle; k++){
    41.       asm("nop");
    42.    }}}
    43. }
    44. void Initialise()
    45. {
    46.  SSPSTAT = 0;
    47.  
    48.  ANSEL = 0b1111;  // Select Channel 0 through to 3 as analogue inputs
    49.  TRISA0 = 1;  // RA0 as input
    50.  TRISA1 = 1;  // RA1 as input
    51.  TRISA2 = 1;  // RA2 as input
    52.  TRISA3 = 1;  // RA3 as input
    53.  TRISB1 = 1;  // SDA as input
    54.  
    55.  ADCON0 = 0b11000001;
    56.  ADCON1 = 0b10000000;
    57.  INTCONbits.PEIE = 1;
    58.  PIE1bits.SSPIE = 1;
    59.  GIE = 1;
    60.  
    61.  TMR0IF=0;
    62.  T0CS=0;
    63.  TMR0IE=0;
    64.  
    65.  TMR0 = 0x00;
    66.  
    67.  OPTION_REG = 0b11001000;
    68. }
    69. void main(void)
    70. {
    71.  Initialise();
    72.  i2c_init();
    73.  while (true)
    74.  {
    75.   i2c_wait();
    76.  }
    77. }
    78. void i2c_write(char byte)
    79. {
    80.    SSPBUF = byte;
    81.    i2c_wait();    
    82. }
    83. void i2c_wait(void)
    84. {
    85.    while ( SSPIF==0 );
    86.    SSPIF = 0;
    87. }
    88. void interrupt isr(void)
    89. {
    90.  unsigned char CASE_SWITCH;
    91.  unsigned char temp;
    92.  unsigned char dataToMaster;
    93.  if (PIR1bits.SSPIF == 1)
    94.  {
    95.   PIR1bits.SSPIF = 0; // reset SSP interrupt flag
    96.   CASE_SWITCH = SSPSTAT & 0x2D;
    97.   asm("nop");
    98.   asm("nop");
    99.   asm("nop");
    100.   asm("nop");
    101.  
    102.   switch(CASE_SWITCH)
    103.   {
    104.    case 0x09 : // master write last byte was addr
    105.    {
    106.     if(SSPCONbits.SSPOV == 0)
    107.     {
    108.      SSPCONbits.CKP = 0;
    109.      temp = SSPBUF; // just dummy value to clear
    110.      SSPCONbits.CKP = 1;
    111.      break;
    112.     }
    113.    }
    114.  
    115.    case 0x29: // master write last byte was data
    116.    {
    117.     if(SSPCONbits.SSPOV == 0)
    118.     {
    119.      SSPCONbits.CKP = 0;
    120.      readValue = SSPBUF; // just dummy value to clear
    121.      SSPCONbits.CKP = 1;
    122.      break;
    123.     }
    124.    }
    125.  
    126.    case 0x0c: // master read last byte was addr
    127.    {
    128.     temp = SSPBUF; // just dummy value to clear
    129.     SSPCONbits.CKP = 0;
    130.     SSPBUF = dataToMaster;
    131.     SSPCONbits.CKP = 1;
    132.     break;
    133.    }
    134.  
    135.    case 0x2c: // master read last byte was data
    136.    {
    137.      temp = SSPBUF; // just dummy value to clear
    138.     SSPCONbits.CKP = 0;
    139.     SSPBUF = dataToMaster;
    140.     SSPCONbits.CKP = 1;
    141.     break;
    142.    }
    143.  
    144.    case 0x28:  // S = 1, R_W = 0, D_A = 1, BF = 0
    145.    {
    146.     temp = SSPBUF; // just dummy value to clear
    147.     // Master NACK
    148.    break;
    149.    }
    150.  
    151.    default:
    152.    break;
    153.   }
    154.  }
    155. }
    156.  
    157.  
    Here is part of the master code, there is a load of USB code that i didn't include as part of this. This function gets called by a flag in the main function, set on a timer interrupt.

    In main() is:
    Code ( (Unknown Language)):
    1.  
    2. writeByteI2C(rw, 0xAE, 0x78 );
    3.  
    the function on the master is:

    Code ( (Unknown Language)):
    1.  
    2. unsigned char writeByteI2C(  unsigned char rw,  unsigned char add,  unsigned char data )
    3. {
    4.  unsigned char result = -1;
    5.  
    6.  if (rw == 0)
    7.  {
    8.   add = 0xFE & add;
    9.  }
    10.  else
    11.  {
    12.   add = 0x01 | add;
    13.  }
    14.  IdleI2C();                          // ensure module is idle
    15.  StartI2C();                         // initiate START condition
    16.  while ( SSPCON2bits.SEN );          // wait until start condition is over
    17.  //WriteI2C( ControlByte );          // write 1 byte - R/W bit should be 0
    18.  IdleI2C();                          // ensure module is idle
    19.  result = WriteI2C( add  );          // write address
    20.  IdleI2C();                          // ensure module is idle
    21.  if (result == 0)
    22.  {
    23.   PORTAbits.RA4 = 1;
    24.   PORTAbits.RA5 = 0;
    25.   result = WriteI2C ( data );     // Write data byte
    26.   if (result == 0)
    27.   {
    28.    PORTAbits.RA4 = 1;
    29.    PORTAbits.RA5 = 0;
    30.   }
    31.   else
    32.   {
    33.    PORTAbits.RA4 = 0;
    34.    PORTAbits.RA5 = 1;
    35.   }
    36.  }
    37.  else
    38.  {
    39.   PORTAbits.RA4 = 0;
    40.   PORTAbits.RA5 = 1;
    41.  }
    42.  IdleI2C();                          // ensure module is idle
    43.  StopI2C();                          // send STOP condition
    44.  while ( SSPCON2bits.PEN );          // wait until stop condition is over
    45.  return ( 0 );                       // return with no error
    46. }
    47.  
    48.  
    I have spent hours looking over forums and there are others with similar problems but nothing has seemed to work, maybe if someone could offer a solution to this it may also help others starting out in I2C between two PICs.

    Thanks for reading,

    Dan.
     
    Last edited: Sep 30, 2011
  2. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    I feel your pain. More like I felt it a few years back when I did a PIC slave device. While I got it to work I don't remember any particulars, except the official documentation was filled with errors.

    FWIW here's my source, done for a p18f4550 on the C18 compiler. I'm not sure this still compiles in the current C18 compiler.

    I believe state 2 is so long and convoluted to convert a "data address" to pointers to where I stored variables so I could read each one out.

    Code ( (Unknown Language)):
    1. void SSPIF_Vector()
    2. {
    3.   _asm
    4.   GOTO SSPIF_Handler
    5.   _endasm
    6. }
    7.  
    8. //#pragma code
    9. //#pragma interrupt SSPIF_Handler
    10. void SSPIF_Handler()
    11. {
    12.     unsigned char  only_imp_bits;            // will store the status of relevant bits of the SSPSTAT
    13.     unsigned char  dummy;                    // place for dummy buffer read
    14.     char I2C_byte;                            // buffer contents will be stored in this
    15.    
    16.     if(PIR1bits.SSPIF == 1)
    17.     {
    18.         only_imp_bits = SSPSTAT & 0b00101101;
    19.         switch(only_imp_bits)
    20.         {
    21.  
    22.             case 0b00001001 :                 // state1:  D/A' = 0; S=1; R/W' =0; BF=1; Master Write Address
    23.                 // WRITE M->S last byte was an address
    24.                   I2C_DataIndex = 0;
    25.                 I2C_byte = SSPBUF;                    // dummy read to clear the 1-byte buffer
    26.                 break;    
    27.              
    28.             case 0b00101001 :                 // state2:  D/A' = 1; S=1; R/W' =0; BF=1; Master Write Data
    29.                 // WRITE M->S last byte was data
    30.                 I2C_byte = SSPBUF;            // read buffer and clear dirty flag
    31.                 if (I2C_DataIndex == 0)
    32.                 {
    33.                     I2C_DataIndex = &szDelayTime1[0];
    34.                     // we have a new virtual data pointer
    35.                     switch (I2C_byte)
    36.                     {
    37.                         case DEVICE_CODE:
    38.                             // virtual address == Timer Code
    39.                             I2C_DataIndex = &TimerType;
    40.                             break;
    41.                         case TIME_STRING1:
    42.                             // virtual address == TIME_STRING1
    43.                             I2C_DataIndex = &szDelayTime1[0];
    44.                             break;
    45.                         case TIME_STRING2:
    46.                             // virtual address == TIME_STRING2
    47.                             I2C_DataIndex = &szDelayTime2[0];
    48.                             break;
    49.                         case TIME_STRING3:
    50.                             // virtual address == TIME_STRING3
    51.                             I2C_DataIndex = &szDelayTime3[0];
    52.                             break;
    53.                         case VUTIL_STRING:
    54.                             // virtual address == VUTIL_STRING
    55.                             I2C_DataIndex = &szVUTIL[0];
    56.                             break;
    57.                         case ICC_STRING:
    58.                             // virtual address == ICC_STRING
    59.                             I2C_DataIndex = &szIcc[0];
    60.                             break;
    61.                         case SPIKE_COUNT:
    62.                             // virtual address == SPIKE_COUNT
    63.                             I2C_DataIndex = &SpikeCount;
    64.                             break;
    65.                         case FAIL_CODE:
    66.                             // virtual address == FAIL_CODE
    67.                             I2C_DataIndex = &FailCode;
    68.                             break;
    69.                         case I2C_MODE:
    70.                             // virtual address == I2C_State
    71.                             I2C_DataIndex = &I2C_State;
    72.                             break;
    73.                         case LED_GREEN:
    74.                             // virtual address == LED_GREEN
    75.                             // we don't use the data, just do the function
    76.                             SET_LED_GREEN;
    77.                             break;
    78.                         case LED_RED:
    79.                             // virtual address == LED_RED
    80.                             // we don't use the data, just do the function
    81.                             SET_LED_RED;
    82.                             break;
    83.                         case LED_OFF:
    84.                             // virtual address == LED_OFF
    85.                             // we don't use the data, just do the function
    86.                             SET_LED_OFF;
    87.                             break;
    88.                         default:
    89.                             // leave I2C_DataIndex as null
    90.                             break;
    91.                     }
    92.                 }
    93.                 else
    94.                 {
    95.                     // we have new data to write
    96.                     *I2C_DataIndex = I2C_byte;
    97.                     I2C_DataIndex++;
    98.                 }
    99.                 break;        
    100.            
    101.             case 0b00001101 :                 // state3:  D/A' = 0; S=1; R/W' =1; BF=1; Master READ Address
    102.                 // READ S->M last byte was address
    103.                 // load SSPBUF with first byte to send
    104.                 if (I2C_DataIndex != 0)
    105.                 {
    106.                     SSPBUF = *I2C_DataIndex;
    107.                     I2C_DataIndex++;
    108.                 }
    109.                 else
    110.                 {
    111.                     SSPBUF = 0;
    112.                 }    
    113.                 break;    
    114.  
    115.             case 0b00101100 :                 /*state4:  D/A' = 1; S=1; R/W' =1; BF=0; Master READ Data*/
    116.                 // READ S->M last byte was data
    117.                 // load SSPBUF with next byte to send
    118.                 SSPBUF = *I2C_DataIndex;
    119.                 I2C_DataIndex++;
    120.                 break;    
    121.  
    122.             case 0b00101000 :                 /*state5:  D/A' = 1; S=1; R/W' =0; BF=0; Master Write NACK*/
    123.                 // master NACK'ed us...
    124.                 break;    
    125.             default:
    126.                 I2C_byte = SSPBUF;                    // dummy read to clear the 1-byte buffer
    127.                 break;    
    128.         }
    129.         SSPCON1bits.CKP = 1;        // release the SCL line if we're holding it down
    130.         PIR1bits.SSPIF = 0;            /*clear the SSPIF bit*/        
    131.         if (SSPCON1bits.SSPOV == 1)
    132.         {
    133.             // we had an overflow
    134.             SSPCON1bits.SSPOV == 1;
    135.             I2C_byte = SSPBUF;            // read buffer and clear dirty flag
    136.         }
    137.     }    
    138. }
    139.  
    140. ///////////////////////////////////////////////////////////////////////////////
     
  3. dtrott

    Thread Starter New Member

    Sep 29, 2011
    2
    0
    Hi ErnieM,

    Thanks for your reply.

    I've managed to get it working a bit more robustly with the removal of the:
    i2c_wait()
    routine out of the main function, which just put the program in a different loop until the SSPIF was set. I was able to remove all of the NOP's. But I don't know any more as to why it is so temperamental.

    Thanks again for reading and your code.

    Dan.
     
Loading...