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 CCS Technical Support

PIC16f887 i2c to slow

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



Joined: 03 Aug 2011
Posts: 4

View user's profile Send private message

PIC16f887 i2c to slow
PostPosted: Wed Aug 03, 2011 8:05 am     Reply with quote

Hi all,

I have been working with the PIC16f887 to try and get I2c slave to work, but I keep running into a speed problem. I need the slave to keep up with a 100kHz master (I am using the diolan u2c-12 as master). The problem is that the slave cant seem to keep up with the master. I have checked the SSPCON register and clock stretching seems to be enabled, but it still wont work. When the master is running at 100kHz, I get some good and some trash data off of the bus. If I drop the master down to 71kHz (for the example code), then I get all good data. I am a new comer to pic processors, so any help would be very much appreciated.

Thanks

Code:

#define DEV_PIC16F887
//#define DEV_PIC16F88

#ifdef DEV_PIC16f887
#include <16F887.h>
#device *=16 adc=8 ICD=TRUE
#fuses INTRC, NOWDT, NOBROWNOUT, PUT, MCLR, HS
#use delay(clock=20000000)

#define SCL1        PIN_C3
#define SDA1        PIN_C4
#define READY_PIN   PIN_A5
#define STATUS_LED  PIN_B5
#define KNOB_PIN    0
#define BUTTON_PIN  PIN_A4

#else //DEV_PIC16f88
#include <16F88.h>
#device *=16 adc=10
#fuses INTRC_IO, NOWDT, NOBROWNOUT, PUT, NOMCLR, HS
#use delay(clock=20000000)

#define SCL1        PIN_B4
#define SDA1        PIN_B1
#define READY_PIN   PIN_A5
#define STATUS_LED  PIN_B2
#define KNOB_PIN    2
#define BUTTON_PIN  PIN_A4

#endif

#byte SSPBUF=0x13
#byte SSPCON=0x14

#bit SSPM0=SSPCON.0
#bit SSPM1=SSPCON.1
#bit SSPM2=SSPCON.2
#bit SSPM3=SSPCON.3
#bit CKP=SSPCON.4
#bit SSPEN=SSPCON.5
#bit SSPOV=SSPCON.6
#bit WCOL=SSPCON.7



#byte SSPCON2=0x91

#bit SEN=SSPCON2.0 //SLAVE
#bit RSEN=SSPCON2.1
#bit PEN=SSPCON2.2
#bit RCEN=SSPCON2.3
#bit ACKEN=SSPCON2.4
#bit ACKDT=SSPCON2.5
#bit ACKSTAT=SSPCON2.6
#bit GCEN=SSPCON2.7



#byte SSPSTAT=0x94

#bit BF=SSPSTAT.0
#bit UA=SSPSTAT.1
#bit RW=SSPSTAT.2
#bit S=SSPSTAT.3
#bit P=SSPSTAT.4
#bit DA=SSPSTAT.5
#bit CKE=SSPSTAT.6
#bit SMP=SSPSTAT.7



#byte PIR1=0x0
#bit SSPIF=PIR1.3


#use i2c(Slave, I2C1, address=0x22, force_hw, stream=slave)

byte const buf[] = {0x48, 0x4, 0x0, 0x12, 0x34, 0x56, 0x78, 0xff,
           0x53, 0x1, 0x0, 0x10, 0xff,
           0x56, 0x1, 0x0, 0x2, 0xff,
           0x4a, 0x0, 0x0};
int pos = 0;

int outsize = 128;
BYTE state, value;

boolean MODE = 0;
byte LASTCHAR;

#INT_SSP
void I2C_isr ()
{
   BYTE state, value;
   
   state = i2c_isr_state(slave);
   
   if((state == 0 ) || (state == 0x80))
   {
      i2c_read(slave); // discard address byte
   }
   
   if (state >= 0x80)
   {
      if (MODE && state == 0x80)
      {
         i2c_write(slave, LASTCHAR);
      }   
      else if (OUTSIZE > 0)
      {
         MODE = true;
         OUTSIZE--;
         LASTCHAR = buf[POS];
         pos++;
         
         i2c_write(slave, LASTCHAR);
      }
      else
      {
         MODE = FALSE;
         i2c_write(slave, 0xff);
      }
   }
   else if (state > 0 && state < 0x80)
   {
      i2c_read(slave);
   }     




int main()
{
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_SSP);

   while (1)
   {
     
   }
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Wed Aug 03, 2011 9:03 am     Reply with quote

Start by getting your CPU clock right.
You are telling the compiler you are running at 20MHz, yet have your first clock fuse as INTRC. The fastest INTRC clock available on either of the chips you are using is just 8MHz. You also have two clock sources selected INTRC, and HS. You can only ever use one.
This may well be leading to you running at a very different speed to what you expect.
Then think 'limit checks'. You have pos being incremented every time you write a byte, but nothing resets it. Also nothing ever resets 'outsize'.
Tidy your code paths up a little, and the chip will run faster.

How long is your bus wiring?. What wire?. What pull-up resistor values are you using?.

Best Wishes
sgtsquatlow



Joined: 03 Aug 2011
Posts: 4

View user's profile Send private message

PostPosted: Wed Aug 03, 2011 11:23 am     Reply with quote

Thanks for the quick reply. I fixed the issue with the clock fuses. I chose the HS since the spec sheet says it should support the 20mhz. My fuses are now:

Code:

#include <16F887.h>
#device *=16 adc=8 ICD=TRUE
#fuses NOWDT, NOBROWNOUT, PUT, MCLR, HS, NOLVP
#use delay(clock=20000000)


Sorry about the code, its actually a very trimmed down version of the original code, so that's why it doesn't reset the variables. I have tried both a 10k and 4.7k resistor. My wires are just regular jumper cables for a breadboard. Length is about 4-6 inches. The problem still exists after correcting the cpu (assuming I did it right this time). I've used a logic analyzer to see that the master is driving the scl at 100kHz.

Thanks again.
sgtsquatlow



Joined: 03 Aug 2011
Posts: 4

View user's profile Send private message

PostPosted: Thu Aug 04, 2011 2:00 pm     Reply with quote

I have managed to get the I2C a little faster by altering the ISR code, but it still isn't up to 100kHz. My ISR looks like this:

Code:

#INT_SSP
void I2C_isr ()
{
   state = i2c_isr_state(slave);

   if((state == 0 ) || (state == 0x80))
   {
      i2c_read(slave); // discard address byte
      //SSPBUF = 0;
     
      if (MODE && state == 0x80)
      {
         i2c_write(slave, LASTCHAR);
         MODE = false;
      }
   }
   
   if (state >= 0x80 && OUTSIZE && PACKETREADY)
   {
      i2c_write(slave, OUTBUF[OUT_READ_POS]);
      LASTCHAR = OUTBUF[OUT_READ_POS];
      OUT_READ_POS = (OUT_READ_POS + 1) % MAX_OUT_BUF_LENGTH;
      OUTSIZE--;
      MODE = true;
   }
   else if (state >= 0x80)
   {
      i2c_write(slave, 0xff);
      MODE = false;
   }
   else if (state > 0)
   {
      INBUF[IN_WRITE_POS] = i2c_read(slave);

      if (state == 2 || state == 3)
      {
         msginlength += (uint16)(INBUF[IN_WRITE_POS] << ((2 - state)*8));
      }

      if (state - 3 == msginlength)
      {
         MSGINREADY = true;
      }

      IN_WRITE_POS = (IN_WRITE_POS + 1) % MAX_IN_BUF_LENGTH;
      INSIZE++;
   }
   
}


I've decided to go with the 1.5k resistors built into the diolan u2c-12. I can get about 83 to 71 kHz on small transfers (about 5 bytes), but when I get up to large transfers (about 256) the data is trash unless i drop the speed down to 61kHz. Also, I'm not sure if it matters but this is the CCS pic16f887 development board.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Thu Aug 04, 2011 2:52 pm     Reply with quote

Are MAX_OUT_BUFFER_LENGTH (and the in buffer length) _binary_ values?.
If not, using % inside the ISR will be fatal.
% operates two ways. For binary sizes, the result is generated using a bit mask, and an & operation. For non binary sizes, a division is used. Results:
1) Interrupts will be disabled if you use the same size division in the external code.
2) Sloth.

The way to perform size limiting inside the ISR, is to simply test if the value is >= the buffer size. If so, set it to zero.

Youcode is still unnecessarily cumbersome. Think about the possible combinations:
=0
>0 <80
=80
>80

Think of how many tests each route takes. Currently it is perhaps twice what is actually needed. Also you can reduce the logic testing by combining the 'packetready' flag, into a top bit of outsize, and then just testing if this value is zero.

Best Wishes
sgtsquatlow



Joined: 03 Aug 2011
Posts: 4

View user's profile Send private message

PostPosted: Fri Aug 05, 2011 1:11 pm     Reply with quote

You are a genius, thank you. I removed the % operator and switched to a value++; value &= mask; and it worked a lot faster. I reconfigured the SSP ISR and removed my timer2 ISR. Now it works great. Thanks again for your help.
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