CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

I2C Master Ignoring Clock-Stretching/Bizarre Behavior

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Seaborgium



Joined: 23 Jul 2012
Posts: 14

View user's profile Send private message

I2C Master Ignoring Clock-Stretching/Bizarre Behavior
PostPosted: Thu Aug 09, 2012 8:03 pm     Reply with quote

I recently had 10-bit I2C communication working perfectly with a PIC16LF1503 master and PIC16F886 slave. The master sent the slave a data address, and the slave replied with the value stored in that data address in an array. You can see the code here.

I ported the master code to a PIC18F4550 and the results were absolutely catastrophic. Suddenly nothing worked anymore. Firstly, for some reason, the master would ignore clock stretching. I had a program in which the master had an i2c_write, and the slave would delay a little while (flashing an LED) before using i2c_read and releasing the clock. Supposedly, the slave would flash the LED and the master's i2c_write would hang while the clock was stretched; then, the slave would release the clock, and the master would continue. However, in practice, the slave/master flashed LEDs at the same time.

I'm almost certain the slave was stretching the clock correctly. I turned bit SEN on, manually cleared CKP at the beginning of the interrupt, even told the slave to output CKP to an LED (the LED displayed 0, so CKP was definitely clear). However, the master continued to ignore clock stretching. All my communications were totally broken unless I manually inserted delays after each I2C command in the master code.

After inserting delays in the code, I managed to get communication kind of working. I had the slave light up LEDs to indicate what point it was at in the I2C communication. I was able to get the slave to recognize its 10-bit address, receive data from the master, and write data to the master (though the master would not receive it correctly).

However, I just tried to slim down the code so I could post it on this forum, and now it doesn't even get that far. But, only LEDs 1 and 2 ever light up, indicating that the slave never gets past the address-recognition part.

I'm tearing my hair out over this. Here's the code. Does anyone know what's wrong?
Master.c:
Code:

#include <main.h>

#use i2c(Master, Slow, I2C1, FORCE_SW, stream=module)

int16 requestData(int16 slaveAddress, unsigned int dataAddress) {
   int16 data;
   int slaveHigh; //High byte of slave address
   int slaveLow; //Low byte of slave address
   
   //Split slave address into low and high byte
   slaveHigh = (slaveAddress>>7)&0xFE; //Get first 2 bits; clear R/W bit
   slaveHigh = slaveHigh|0xF0; //"11110XXX" signifies 10-bit address; add this.
   slaveLow = slaveAddress&0xFF; //Get lower 8 bits
   
   //Instruct slave that you wish to write
   delay_ms(1);
   i2c_start(module);
   delay_ms(1);
   i2c_write(module, slaveHigh); //High byte
   delay_ms(1);
   i2c_write(module, slaveLow); //Low byte
   delay_ms(100);
   
   //Give data address
   i2c_write(module, dataAddress);
   delay_ms(100);
   
   //Restart; instruct slave that you wish to read
   i2c_start(module);
   delay_ms(100);
   
   i2c_write(module, slaveHigh|1); //High byte with R/W set
   delay_ms(100);
   
   //Receive data and finish
   data=i2c_read(module, 1);//<<8; //Read high byte and ACK
   //delay_ms(1);
   //data+=i2c_read(module, 0); //Read low byte and end transmission
   i2c_stop(module);
   
   return data;
}

//Calculates the slave address and data address corresponding to
//a specific sensor number, then gets that sensor value.
int16 requestSensorVal(int16 sensorNum) {
   int16 slaveAddress;
   unsigned int dataAddress;
   dataAddress=sensorNum%16;
   slaveAddress=(sensorNum-dataAddress)/16;
   return requestData(slaveAddress, dataAddress);
}

void main() {
   
   while(true) {
      delay_ms(1000);
      requestData(0x0, 0);
   }


Master.h:
Code:
#include <18F4550.h>
#device adc=16

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES CPUDIV1                  //No system clock postscaler
#FUSES HSPLL                    //High speed xtal
#FUSES PLL5                     //??
#FUSES NOPROTECT                //No code pritection
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#use delay(clock=48000000) //Configured for 48 MHz


Slave.c:
Code:
#include <Slave.h>

//Define output pins
#define LED PIN_A0
#define LED2 PIN_A1
#define LED3 PIN_A2
#define LED4 PIN_A3
#define LED5 PIN_A4
#define LED0 PIN_A5

//#define MOSFET PIN_C5


//Define addressing state
#define NOADDR 0 //Before being assigned an address
#define GOTADDRHIGH 1 //Has received high byte of address
#define GOTADDR 2 //Has been assigned address

//General call bit
#BYTE SSPCON2=0x91
#BIT GCEN=SSPCON2.7
#BIT SEN=SSPCON2.0

//10-bit addressing bits
#BYTE SSPCON=0x14
#BIT SSPM0=SSPCON.0
#BIT CKP=SSPCON.4

#BYTE SSPSTAT=0x94
#BIT UPDATEADDR=SSPSTAT.1
#BIT RWBIT=SSPSTAT.2


//Change # of sensors per module to correct number in sensorVals

int writeBuffer [2]; //Holds one 10-bit integer to write through i2c
int16 sensorVals [16];
int addrState; //State of address assignment

//10-bit addressing stuff
int1 firstByteReceived; //Records whether first byte of 10-bit address has been received
int16 addrHigh; //High byte of address
int16 addrLow; //Low byte of address

void flash(int ledsToFlash) {
   output_high(LED0);
   if ((ledsToFlash&0b1)==0b1)
      output_high(LED);
   if ((ledsToFlash&0b10)==0b10)
      output_high(LED2);
   if ((ledsToFlash&0b100)==0b100)
      output_high(LED3);
   if ((ledsToFlash&0b1000)==0b1000)
      output_high(LED4);
   if ((ledsToFlash&0b10000)==0b10000)
      output_high(LED5);
   delay_ms(400);
   output_low(LED);
   output_low(LED2);
   output_low(LED3);
   output_low(LED4);
   output_low(LED5);
   output_low(LED0);
   delay_ms(200);
}

#int_SSP
void i2c_interrupt() {
   if (UPDATEADDR) { //Just received one of two start bytes
      if (!firstByteReceived) { //Just received first byte
         firstByteReceived=TRUE;
         i2c_slaveAddr(addrLow); //Set low byte
         output_high(LED);
         i2c_read();
      }
      else { //Just received second byte
         firstByteReceived=FALSE;
         i2c_slaveAddr(addrHigh); //Set high byte
         output_high(LED2);
         i2c_read();
      }
      return;
   }
   
   //Get state
   int state;
   state = i2c_isr_state();
   
   if (state==0||state==0x80) { //Master has issued start command
      i2c_read(); //Clear address from read buffer.
   }
   
   if (RWBIT==TRUE) { //Master is waiting for data
      output_high(LED4);
      i2c_write(0); //Write byte
      //i2c_write(writeBuffer[state&0x01111111]); //Write appropriate byte, based on how many have already been written
   }
   
   else if ((state&0b01111111)>0) { //Master has sent data; read.
      if (addrState==GOTADDR) {
         output_high(LED3);
         i2c_read();
         //You already have an address; this must be a data request.
         //int16 toSend;
         
         //toSend=sensorVals[i2c_read()]; //Grab appropriate sensor value.
         //writeBuffer[0] = toSend>>8;
         //writeBuffer[1] = toSend&0xFF;
      }
      else if (addrState==NOADDR) {
         //Have not been assigned an address yet
         //This is the first byte of the address
         addrState=GOTADDRHIGH;
         addrHigh=i2c_read(); //Get high byte data
      }
      else if (addrState==GOTADDRHIGH) {
         //Have been assigned high byte of address
         //Message is the low byte of the address
         addrState=GOTADDR;
         GCEN=FALSE; //Stop responding to general calls
         
         i2c_slaveAddr(addrHigh); //Set address to high byte
         //output_low(MOSFET); //Open up transistor gate
         addrLow=i2c_read();
      }
   }
}

void main() {
   SEN=TRUE; //Allow clock stretching
   
   setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
   
   //output_high(MOSFET); //Lock up the gate immediately
   
   enable_interrupts(INT_SSP); //Enable interrupts
   enable_interrupts(GLOBAL);
   
   GCEN=TRUE; //Allow general calls
   SSPM0=TRUE; //10-bit addressing
   
   firstByteReceived=FALSE;
   //addrState=NOADDR; //Have not been assigned an address yet
   addrState=GOTADDR;
   
   while (true) { //Constantly refresh sensor values
      sensorVals[0]=0;
      sensorVals[1]=1;
      sensorVals[2]=2;
      sensorVals[3]=3;
      sensorVals[4]=4;
      sensorVals[5]=5;
      sensorVals[6]=6;
      sensorVals[7]=7;
   }
}


Slave.h:
Code:
#include <16F886.h>
#device adc=16

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOPROTECT                //No read protection
#FUSES NOWRT                    //No write protection

#use delay(int=250000)

#use i2c(Slave, Slow, sda=PIN_C4, scl=PIN_C3, force_hw, address=0xA0)
Ttelmah



Joined: 11 Mar 2010
Posts: 19348

View user's profile Send private message

PostPosted: Fri Aug 10, 2012 2:13 am     Reply with quote

Have you looked at the chip errata?.
I think number 40, on the 'family' sheet might be the problem....

Best Wishes
Seaborgium



Joined: 23 Jul 2012
Posts: 14

View user's profile Send private message

PostPosted: Fri Aug 10, 2012 10:58 am     Reply with quote

Hm, I see. However, the errata sheet says that the solution would be to prevent clock stretching. If I can't stretch the clock, how can I make sure the slave has enough time to retrieve data from the sensor array, etc.? Is the only solution to manually add delays in the master code, or use a different microchip without this problem?

Thank you so much for your help! This has been really frustrating.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Aug 10, 2012 11:15 am     Reply with quote

Quote:
Have you looked at the chip errata?.
I think number 40, on the 'family' sheet might be the problem....

I don't think that's it, because he's doing a software master:
Code:

Master.c:

#use i2c(Master, Slow, I2C1, FORCE_SW, stream=module)
 

In the "family" errata, item 40 is referring to a hardware MSSP master:
http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010300
But he's doing bit-banging i2c so it doesn't apply.



Also, this test program is invalid because you've commented out the
NACK line, which is required by the i2c spec on the last i2c read in the
master program:
Quote:

//Receive data and finish
data=i2c_read(module, 1);//<<8; //Read high byte and ACK
//delay_ms(1);
//data+=i2c_read(module, 0); //Read low byte and end transmission
i2c_stop(module);

return data;
}




In your Slave program you are declaring a variable in the middle of code,
which is not supported in CCS. Move it to the start of the function:
Quote:

//Get state
int state;
state = i2c_isr_state();



Furthermore, during initial testing I would not run my Slave PIC at 250
KHz. Change it to 8 MHz. Get it working and then consider lowering
the slave frequency.
Ttelmah



Joined: 11 Mar 2010
Posts: 19348

View user's profile Send private message

PostPosted: Fri Aug 10, 2012 3:24 pm     Reply with quote

I had missed he was doing software I2C on the master.

I'd guess the problem is _speed_. The older implementation with the LF1503 master, was probably running significantly slower than the 4550. With the slave only running at 250KHz, it probably does not assert the stretching, till after the 4550 has already tested the line. With nearly 200:1 clock speed difference, I'd expect problems. Try raising the clock speed of the slave, so it is proportionately 'as fast' relative to the master, as on the older system.

Generally, There often seem to be problems if there is a very wide disparity between the device speeds.

Best Wishes
Seaborgium



Joined: 23 Jul 2012
Posts: 14

View user's profile Send private message

PostPosted: Fri Aug 10, 2012 6:45 pm     Reply with quote

Ah! Sorry, I lowered the speed when I was trying to test current draw vs. clock speed, and I forgot to raise it again.
Thanks for the help! I'll increase the speed, make those other modifications, and try again.
Ttelmah



Joined: 11 Mar 2010
Posts: 19348

View user's profile Send private message

PostPosted: Sun Aug 12, 2012 7:49 am     Reply with quote

Just did a minimal 'start, send byte, stop' routine coded for the 4550, and can confirm, the compiler _does_ correctly wait for the SCL line to be released:
Code:

0052:  BSF    F93.1 //float the SCL line
0054:  BTFSS  F81.1 //See if it goes high
0056:  BRA    0054 //loop if not


It waits here if the clock line is being held low. This is called 6uSec after it sends the last edge of the last data bit (at 48MHz).

So the question then is how long the 886 will take to assert this condition, running at 250K?. The picture show, that the low, is not asserted, till the interrupt flag is set. This is at the start of the next machine cycle _after_ the event. Instruction clock is 250K/4 = 62500Hz, and the worst case would potentially be if the event occurred just after the previous T1 cycle, giving just under 32uSec, and best case just on 16uSec.
This also allows us to give a figure to 'minimum speed for slave', of 1.33Mhz, with a master at 48MHz. So basically no slower than master/36, explaining why very wide speed differences do give problems with I2C.....
So slave at 2MHz, should work.

Best Wishes
Seaborgium



Joined: 23 Jul 2012
Posts: 14

View user's profile Send private message

PostPosted: Mon Aug 13, 2012 11:17 am     Reply with quote

I changed the clock back to the original (8 MHz), and it seems to be working fine so far (I'm still in the process of getting it back to its previous functionality).

However, this information is quite valuable, since I'm also considering how low of a clock speed I can use - I'm trying to reduce current draw.

Again, thank you so much!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group