|
|
View previous topic :: View next topic |
Author |
Message |
sgtsquatlow
Joined: 03 Aug 2011 Posts: 4
|
PIC16f887 i2c to slow |
Posted: Wed Aug 03, 2011 8:05 am |
|
|
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
|
|
Posted: Wed Aug 03, 2011 9:03 am |
|
|
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
|
|
Posted: Wed Aug 03, 2011 11:23 am |
|
|
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
|
|
Posted: Thu Aug 04, 2011 2:00 pm |
|
|
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
|
|
Posted: Thu Aug 04, 2011 2:52 pm |
|
|
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
|
|
Posted: Fri Aug 05, 2011 1:11 pm |
|
|
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. |
|
|
|
|
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
|