View previous topic :: View next topic |
Author |
Message |
JaSoN
Joined: 05 Jul 2006 Posts: 15
|
strange data receive from I2C |
Posted: Mon Jul 31, 2006 8:02 am |
|
|
Hi, I am programming I2C network on PIC16F877 microcontroller. The I2C network is built by 3 pics, 1 master with 2 slaves.
My program requires continuously receiving data from 2 slaves and each slave sends 4 bytes data to master.
It works fine if I do not ask for receiving data frequently. but, overflow data would be received from one of the slave, if I want the receive the data frequently.
Could anyone tell me why do this happen? |
|
![](templates/subSilver/images/spacer.gif) |
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon Jul 31, 2006 9:03 am |
|
|
Code! Give us some code to look at. |
|
![](templates/subSilver/images/spacer.gif) |
JaSoN
Joined: 05 Jul 2006 Posts: 15
|
|
Posted: Mon Jul 31, 2006 9:10 am |
|
|
This is the code of the loop, it receives data from slave address 0x1E for the steering data and then read from slave address 0x0A for drive data. but the drive would return 255 after a numbers of data is read and the steering data still can return correctly!
I2Creceive(0X1E);
if(I2C_receive[4]==0)
{
slave_address=0x0A;
COMMAND[0]=0x56;
command_value=50;
I2Csend(slave_address,COMMAND[0],command_value);
}
if(I2C_receive[4]>0)
{
slave_address=0x0A;
COMMAND[0]=0x56;
command_value=130;
I2Csend(slave_address,COMMAND[0],command_value);
}
track_status[0]=I2C_receive[4];
STEERING=256*I2C_receive[0]+I2C_receive[1];
THRES=I2C_receive[2];
STEERINGlevel=I2C_receive[3];
//define cases to read data from drive module
if(track_status[0]==1 && track_status[1]==0)
{ speedread=1; }
else if (track_status[0]==0 && track_status[1]==1)
{ speedread=1; }
else speedread=0;
//if(speedread==1)
//{
slave_address=0x0A;
I2Creceive(slave_address);
DIS=256*I2C_receive[0]+I2C_receive[1];
SPEED=I2C_receive[2];
PWM=I2C_receive[3];
//distance data contains 4 characters
if (DIS<10>=0)
printf("\r\n0000%ld",DIS);
else if (DIS<100>9)
printf("\r\n000%ld",DIS);
else if (DIS<1000>99)
printf("\r\n00%ld",DIS);
else if (DIS<10000>999)
printf("\r\n0%ld",DIS);
else if (DIS>9999)
printf("\r\n%ld",DIS);
//speed data contains 3 characters
if (SPEED<10>9)
printf("\r\n00%ld",SPEED);
else if (SPEED<100>=0)
printf("\r\n0%ld",SPEED);
else printf("\r\n%Ld",SPEED);
printf("\r\n%Ld",PWM);
printf("\r\n%Ld", STEERING);
if (track_status[0]<100>=0)
{ printf("\r\n0%d",track_status[0]); }
else printf("\r\n%d", track_status[0]); |
|
![](templates/subSilver/images/spacer.gif) |
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon Jul 31, 2006 3:46 pm |
|
|
Quote: | I2Creceive(0X1E);
I2Csend(slave_address,COMMAND[0],command_value);
|
I appears that you have created your own I2C routines so I have no idea of how you are trying to talk to the Slaves. You need to post your code for I2Creceive() and I2Csend().
Ronald |
|
![](templates/subSilver/images/spacer.gif) |
JaSoN
Joined: 05 Jul 2006 Posts: 15
|
|
Posted: Tue Aug 01, 2006 3:12 am |
|
|
thanks..here are the send and receive routines
void I2Csend(int slave_address, int I2C_command, int16 command_value)
{
char I2C_send;
char I2C_sendhi,I2C_sendlo;
I2C_sendhi=0;
I2C_sendlo=0;
I2C_send=command_value;
i2c_start(); i2c_write(slave_address);
i2c_write(I2C_command);
i2c_write(I2C_send);
i2c_stop();
}
void I2Creceive(int slave_address)
{
if (slave_address==0x0A)
{ M=2; }
else
{ M=4; }
for(I=0;I<=M;I++)
{
i2c_start(); i2c_write(slave_address+1); delay_us(20);
I2C_receive[I]=i2c_read(0);
i2c_stop(); }
} |
|
![](templates/subSilver/images/spacer.gif) |
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Tue Aug 01, 2006 8:54 am |
|
|
One thing I see is that you are passing int variables to your I2CSend() routines and then stuffing them into char variables that are declared inside it. Not sure how the conversion will be handled here.
Next, you are sending the i2c_write() commands one right after another with no delays. The Slaves, which are PIC's themselves, will take time to realize that it has an I2C interrupt that needs to be serviced. Then, it will need to actually service that routine. This takes time and you're not allowing the Master to give the Slave this time. The bigger the Slave ISR the more time that it will need. In your Slave code you should have CKP, which controls the SCL line, set to zero(0) so it will hold the clock line low, which tells the Master that the Slave is busy, until it's done taking care of each i2c_write() then set CKP back to a high(1) so the Master knows that it can send the next i2c_write(). Your Master code should be written so that it will know how to handle this. I believe if you're using the I2C hardware the next i2c_write() will be sent once the SCL line has been released by the Slave.
An oscilloscope would be extremely handy in monitoring the signals so you can see if you are getting the ACK signal back properly. Your code isn't checking to see if the Slave is talking or not.
You should have something like:
Code: |
i2c_start();
if(!i2c_write(address))// this checks to see if the Slave ACK'd back before
{ // trying to write another command
i2c_write(command);
}
i2c_stop();
|
The i2c_write() returns the ACK bit. If you get a NOACK (no response) then it will do no good to continue writing commands to the Slave because it's not listening. Clear a mud?
Ronald |
|
![](templates/subSilver/images/spacer.gif) |
JaSoN
Joined: 05 Jul 2006 Posts: 15
|
|
Posted: Wed Aug 02, 2006 1:41 am |
|
|
Thanks very much..
I want to ask how can I determine whether the slave PIC is busy or not? |
|
![](templates/subSilver/images/spacer.gif) |
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Wed Aug 02, 2006 3:10 pm |
|
|
In normal I2C parts, ie. temp sensors, eeproms....., the Slave will hold the SCL line LOW while it's doing whatever it is that needs to be done. Then the Slave releases the SCL line (allows it to float high) which the Master detects and then knows that it can send another command to the Slave. If you are using the I2C Hardware then the Master should know how to handle this. If you are bit-banging(let's see if we can get you confused now) then you'll have to put something in your code to test the SCL line if it's being held low by the Slave.
Simply try making a circuit and test things out. Send a Start, Write, Write and a Stop. Hold the SCL line Low and watch how it will 'hang' after the first Write command. The Master will think that the Slave is holding the SCL line low and it will wait until it is allowed to float High before sending the second Write command.
You have mountains of information in the forum on I2C available so search and study. Read the spec. sheet on the I2C protocol that can be found on www.philips.com web site. Don't expect much more help until you have beat your head against your monitor for a while trying to figure out how to make it work. That's the way you remember things the best.
Ronald |
|
![](templates/subSilver/images/spacer.gif) |
|