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

i2c read from PIC to PIC

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







i2c read from PIC to PIC
PostPosted: Wed Jul 16, 2003 6:45 am     Reply with quote

I have managed to get two PIC16F877s connected via i2c. I have written code on the master side that sends data to the slave. The slave is only there to test if the master is operating correctly as an i2c device, nothing more. Hence I don't need to implement a "proper" i2c slave with my code.

I have successfully tested master to slave transmissions, but I am having trouble with reading from the slave. Note that the slave is receiving data from my PC via RS232 and it sends it to the slave via i2c.

I have specified the address of the slave as 0x00. Now when I attempt to read from the slave, the master sends an address of 0x01 on the i2c bus, meaning the R/W bit is 1 (hence read). However, I think the slave is not recognizing that it is being addressed... I know the interrupt is being called when the master does i2c_write(address) (=0x01), but the slave's i2c_poll() in the ISR fails (presumably because it does not think it is being addressed when the master sends 0x01).

Do I need to change the slave's address in some way that it will understand it is being addressed when a slave to master transmission is needed? Any other ideas?

The relevant pieces of code are below:

MASTER
----------------------------------------------
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use rs232(baud=57600, xmit=PIN_C6, rcv=PIN_C7)
#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)

// and somewhere in main():

address = buffer[0]; // Buffer contains data via RS232

if(bit_test(address,0)) { // test for read from slave operation

i2c_start();
i2c_write(address);// address I send is 0x01 i.e. R/W bit is 1
read_data = i2c_read(0);
i2c_stop();
putc(read_data);
delay_ms(1000);
}


SLAVE
---------------------------------------

#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=20000000)
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x00, FORCE_HW)

#INT_SSP
void ssp_interupt () {

if(i2c_poll()==TRUE) {

if (i==0)
address=i2c_read();
else if (i==1)
data=i2c_read();
else if (i==2)
data2=i2c_read();

i++;

}
}

// and somewhere in main()....

if(bit_test(address,0)) { // test if this is a read operation

i2c_write(0b10101010); // send byte back to master

}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516058
Jose
Guest







Re: i2c read from PIC to PIC
PostPosted: Wed Jul 16, 2003 7:03 am     Reply with quote

0x00 is not a valid address. It is used for general calls. Try changing slave address to some other value.

:=I have managed to get two PIC16F877s connected via i2c. I have written code on the master side that sends data to the slave. The slave is only there to test if the master is operating correctly as an i2c device, nothing more. Hence I don't need to implement a "proper" i2c slave with my code.
:=
:=I have successfully tested master to slave transmissions, but I am having trouble with reading from the slave. Note that the slave is receiving data from my PC via RS232 and it sends it to the slave via i2c.
:=
:=I have specified the address of the slave as 0x00. Now when I attempt to read from the slave, the master sends an address of 0x01 on the i2c bus, meaning the R/W bit is 1 (hence read). However, I think the slave is not recognizing that it is being addressed... I know the interrupt is being called when the master does i2c_write(address) (=0x01), but the slave's i2c_poll() in the ISR fails (presumably because it does not think it is being addressed when the master sends 0x01).
:=
:=Do I need to change the slave's address in some way that it will understand it is being addressed when a slave to master transmission is needed? Any other ideas?
:=
:=The relevant pieces of code are below:
:=
:=MASTER
:=----------------------------------------------
:=#include <16F877.h>
:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=#use delay(clock=20000000)
:=#use rs232(baud=57600, xmit=PIN_C6, rcv=PIN_C7)
:=#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
:=
:=// and somewhere in main():
:=
:=address = buffer[0]; // Buffer contains data via RS232
:=
:=if(bit_test(address,0)) { // test for read from slave operation
:=
:=i2c_start();
:=i2c_write(address);// address I send is 0x01 i.e. R/W bit is 1
:=read_data = i2c_read(0);
:=i2c_stop();
:=putc(read_data);
:=delay_ms(1000);
:=}
:=
:=
:=SLAVE
:=---------------------------------------
:=
:=#include <16F877.h>
:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=#use delay(clock=20000000)
:=#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x00, FORCE_HW)
:=
:=#INT_SSP
:=void ssp_interupt () {
:=
:= if(i2c_poll()==TRUE) {
:=
:= if (i==0)
:= address=i2c_read();
:= else if (i==1)
:= data=i2c_read();
:= else if (i==2)
:= data2=i2c_read();
:=
:= i++;
:=
:= }
:=}
:=
:=// and somewhere in main()....
:=
:=if(bit_test(address,0)) { // test if this is a read operation
:=
:=i2c_write(0b10101010); // send byte back to master
:=
:=}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516059
R.J.Hamlett
Guest







Re: i2c read from PIC to PIC
PostPosted: Wed Jul 16, 2003 7:36 am     Reply with quote

:=I have managed to get two PIC16F877s connected via i2c. I have written code on the master side that sends data to the slave. The slave is only there to test if the master is operating correctly as an i2c device, nothing more. Hence I don't need to implement a "proper" i2c slave with my code.
:=
:=I have successfully tested master to slave transmissions, but I am having trouble with reading from the slave. Note that the slave is receiving data from my PC via RS232 and it sends it to the slave via i2c.
:=
:=I have specified the address of the slave as 0x00. Now when I attempt to read from the slave, the master sends an address of 0x01 on the i2c bus, meaning the R/W bit is 1 (hence read). However, I think the slave is not recognizing that it is being addressed... I know the interrupt is being called when the master does i2c_write(address) (=0x01), but the slave's i2c_poll() in the ISR fails (presumably because it does not think it is being addressed when the master sends 0x01).
:=
:=Do I need to change the slave's address in some way that it will understand it is being addressed when a slave to master transmission is needed? Any other ideas?
:=
:=The relevant pieces of code are below:
:=
:=MASTER
:=----------------------------------------------
:=#include <16F877.h>
:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=#use delay(clock=20000000)
:=#use rs232(baud=57600, xmit=PIN_C6, rcv=PIN_C7)
:=#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
:=
:=// and somewhere in main():
:=
:=address = buffer[0]; // Buffer contains data via RS232
:=
:=if(bit_test(address,0)) { // test for read from slave operation
:=
:=i2c_start();
:=i2c_write(address);// address I send is 0x01 i.e. R/W bit is 1
:=read_data = i2c_read(0);
:=i2c_stop();
:=putc(read_data);
:=delay_ms(1000);
:=}
:=
:=
:=SLAVE
:=---------------------------------------
:=
:=#include <16F877.h>
:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=#use delay(clock=20000000)
:=#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x00, FORCE_HW)
:=
:=#INT_SSP
:=void ssp_interupt () {
:=
:= if(i2c_poll()==TRUE) {
:=
:= if (i==0)
:= address=i2c_read();
:= else if (i==1)
:= data=i2c_read();
:= else if (i==2)
:= data2=i2c_read();
:=
:= i++;
:=
:= }
:=}
:=
:=// and somewhere in main()....
:=
:=if(bit_test(address,0)) { // test if this is a read operation
:=
:=i2c_write(0b10101010); // send byte back to master
:=
:=}
One obvious big problem here, is time...
Remember that the I2C interrupt, with data available, will occur once the last bit of the data is seen. Even at 20MHz, there will be a latency of a few uSec, before you actually get to the handler code, and this itself will take a few uSec more to execute. Then the main code, presumably contains other routines, in the loop, waiting to look at the address. The data is only finally written back to the I2C port, once all these times have executed. Your master routine, starts reading the returned data immediately. Unfortunately, I would suspect, that the data is not actually ready yet...
I had similar problems with SPI, and it was salutory, to work out the 'worst case' response times (where the second chip, was doing other things like servicing serial interrupts as well).
I think I'd stick the return into the interrupt service routine if possible (have a buffer containing the data to be sent?), and add a delay between sending the data, and looking for the return, and see if things improve.
However I think the reason for your problem, may be that the 'address' byte, in the buffer, doesn't have the low bit set as you expect!.
I'm not quite sure how this behaves (I'm really an SPI man, rather than slave I2C), but if you read the chips data sheet, it comments that the status of the direction bit, gets copied into the R/W bit of the SSPSTAT register, and the received address, gets transferred to the receive buffer. Now it is possible that the address in this context, is the seven bit value used for address matching, and not the eight bit data...
The other comment, is that I'd be very 'careful' about using the CCS code inside the IRQ. Instead I'd go 'DIY' in the handler, with something like:
#bit DIRN=0x94,2
#byte SSPBUF=0x13
#bit BUFFERFULL=0x94,0
#bit OVERFLOW=0x14,6
#INT_SSP
void ssp_interupt () {
//Technically if you arrrive here, a byte _is_ waiting, hence
//there is no need to poll the chip
if (OVERFLOW) OVERFLOW=0;
if (i==0) {
if (DIRN) {
address=SSPBUF;
SSPBUF=0b10101010;
}
}
else if (i==1)
data=SSPBUF;
else if (i==2)
data2=SSPBUF;
i++;
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516062
Stefanos Dris
Guest







Re: i2c read from PIC to PIC
PostPosted: Wed Jul 16, 2003 8:23 am     Reply with quote

:=:=I have managed to get two PIC16F877s connected via i2c. I have written code on the master side that sends data to the slave. The slave is only there to test if the master is operating correctly as an i2c device, nothing more. Hence I don't need to implement a "proper" i2c slave with my code.
:=:=
:=:=I have successfully tested master to slave transmissions, but I am having trouble with reading from the slave. Note that the slave is receiving data from my PC via RS232 and it sends it to the slave via i2c.
:=:=
:=:=I have specified the address of the slave as 0x00. Now when I attempt to read from the slave, the master sends an address of 0x01 on the i2c bus, meaning the R/W bit is 1 (hence read). However, I think the slave is not recognizing that it is being addressed... I know the interrupt is being called when the master does i2c_write(address) (=0x01), but the slave's i2c_poll() in the ISR fails (presumably because it does not think it is being addressed when the master sends 0x01).
:=:=
:=:=Do I need to change the slave's address in some way that it will understand it is being addressed when a slave to master transmission is needed? Any other ideas?
:=:=
:=:=The relevant pieces of code are below:
:=:=
:=:=MASTER
:=:=----------------------------------------------
:=:=#include <16F877.h>
:=:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=:=#use delay(clock=20000000)
:=:=#use rs232(baud=57600, xmit=PIN_C6, rcv=PIN_C7)
:=:=#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
:=:=
:=:=// and somewhere in main():
:=:=
:=:=address = buffer[0]; // Buffer contains data via RS232
:=:=
:=:=if(bit_test(address,0)) { // test for read from slave operation
:=:=
:=:=i2c_start();
:=:=i2c_write(address);// address I send is 0x01 i.e. R/W bit is 1
:=:=read_data = i2c_read(0);
:=:=i2c_stop();
:=:=putc(read_data);
:=:=delay_ms(1000);
:=:=}
:=:=
:=:=
:=:=SLAVE
:=:=---------------------------------------
:=:=
:=:=#include <16F877.h>
:=:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=:=#use delay(clock=20000000)
:=:=#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x00, FORCE_HW)
:=:=
:=:=#INT_SSP
:=:=void ssp_interupt () {
:=:=
:=:= if(i2c_poll()==TRUE) {
:=:=
:=:= if (i==0)
:=:= address=i2c_read();
:=:= else if (i==1)
:=:= data=i2c_read();
:=:= else if (i==2)
:=:= data2=i2c_read();
:=:=
:=:= i++;
:=:=
:=:= }
:=:=}
:=:=
:=:=// and somewhere in main()....
:=:=
:=:=if(bit_test(address,0)) { // test if this is a read operation
:=:=
:=:=i2c_write(0b10101010); // send byte back to master
:=:=
:=:=}
:=One obvious big problem here, is time...
:=Remember that the I2C interrupt, with data available, will occur once the last bit of the data is seen. Even at 20MHz, there will be a latency of a few uSec, before you actually get to the handler code, and this itself will take a few uSec more to execute. Then the main code, presumably contains other routines, in the loop, waiting to look at the address. The data is only finally written back to the I2C port, once all these times have executed. Your master routine, starts reading the returned data immediately. Unfortunately, I would suspect, that the data is not actually ready yet...
:=I had similar problems with SPI, and it was salutory, to work out the 'worst case' response times (where the second chip, was doing other things like servicing serial interrupts as well).
:=I think I'd stick the return into the interrupt service routine if possible (have a buffer containing the data to be sent?), and add a delay between sending the data, and looking for the return, and see if things improve.
:=However I think the reason for your problem, may be that the 'address' byte, in the buffer, doesn't have the low bit set as you expect!.
:=I'm not quite sure how this behaves (I'm really an SPI man, rather than slave I2C), but if you read the chips data sheet, it comments that the status of the direction bit, gets copied into the R/W bit of the SSPSTAT register, and the received address, gets transferred to the receive buffer. Now it is possible that the address in this context, is the seven bit value used for address matching, and not the eight bit data...
:=The other comment, is that I'd be very 'careful' about using the CCS code inside the IRQ. Instead I'd go 'DIY' in the handler, with something like:
:=#bit DIRN=0x94,2
:=#byte SSPBUF=0x13
:=#bit BUFFERFULL=0x94,0
:=#bit OVERFLOW=0x14,6
:=#INT_SSP
:=void ssp_interupt () {
:=//Technically if you arrrive here, a byte _is_ waiting, hence
:=//there is no need to poll the chip
:= if (OVERFLOW) OVERFLOW=0;
:= if (i==0) {
:= if (DIRN) {
:= address=SSPBUF;
:= SSPBUF=0b10101010;
:= }
:= }
:= else if (i==1)
:= data=SSPBUF;
:= else if (i==2)
:= data2=SSPBUF;
:= i++;
:=}
:=

OK, but I know the ISR is being called since I added a line that switches LEDs on at the beginning of the ISR. Now, if, as you said, there is no need to poll the device, why does i2c_poll() fail? I know this happens because I change the LEDs output inside the if(i2c_poll()){ /* ........ */ }.

Also, I am able to write to the slave easily, using the CCS functions inside the slave's ISR! This is why I think the slave is not recognizing it is being addressed when you change the address byte to 0x01 from 0x00. Does that make sense? Will the ISR be called even if the address sent doe NOT match the slave's addresS?

I will try your DYI routine and see if that works...
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516065
R.J.Hamlett
Guest







Re: i2c read from PIC to PIC
PostPosted: Wed Jul 16, 2003 9:12 am     Reply with quote

:=:=:=I have managed to get two PIC16F877s connected via i2c. I have written code on the master side that sends data to the slave. The slave is only there to test if the master is operating correctly as an i2c device, nothing more. Hence I don't need to implement a "proper" i2c slave with my code.
:=:=:=
:=:=:=I have successfully tested master to slave transmissions, but I am having trouble with reading from the slave. Note that the slave is receiving data from my PC via RS232 and it sends it to the slave via i2c.
:=:=:=
:=:=:=I have specified the address of the slave as 0x00. Now when I attempt to read from the slave, the master sends an address of 0x01 on the i2c bus, meaning the R/W bit is 1 (hence read). However, I think the slave is not recognizing that it is being addressed... I know the interrupt is being called when the master does i2c_write(address) (=0x01), but the slave's i2c_poll() in the ISR fails (presumably because it does not think it is being addressed when the master sends 0x01).
:=:=:=
:=:=:=Do I need to change the slave's address in some way that it will understand it is being addressed when a slave to master transmission is needed? Any other ideas?
:=:=:=
:=:=:=The relevant pieces of code are below:
:=:=:=
:=:=:=MASTER
:=:=:=----------------------------------------------
:=:=:=#include <16F877.h>
:=:=:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=:=:=#use delay(clock=20000000)
:=:=:=#use rs232(baud=57600, xmit=PIN_C6, rcv=PIN_C7)
:=:=:=#use I2C(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
:=:=:=
:=:=:=// and somewhere in main():
:=:=:=
:=:=:=address = buffer[0]; // Buffer contains data via RS232
:=:=:=
:=:=:=if(bit_test(address,0)) { // test for read from slave operation
:=:=:=
:=:=:=i2c_start();
:=:=:=i2c_write(address);// address I send is 0x01 i.e. R/W bit is 1
:=:=:=read_data = i2c_read(0);
:=:=:=i2c_stop();
:=:=:=putc(read_data);
:=:=:=delay_ms(1000);
:=:=:=}
:=:=:=
:=:=:=
:=:=:=SLAVE
:=:=:=---------------------------------------
:=:=:=
:=:=:=#include <16F877.h>
:=:=:=#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
:=:=:=#use delay(clock=20000000)
:=:=:=#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0x00, FORCE_HW)
:=:=:=
:=:=:=#INT_SSP
:=:=:=void ssp_interupt () {
:=:=:=
:=:=:= if(i2c_poll()==TRUE) {
:=:=:=
:=:=:= if (i==0)
:=:=:= address=i2c_read();
:=:=:= else if (i==1)
:=:=:= data=i2c_read();
:=:=:= else if (i==2)
:=:=:= data2=i2c_read();
:=:=:=
:=:=:= i++;
:=:=:=
:=:=:= }
:=:=:=}
:=:=:=
:=:=:=// and somewhere in main()....
:=:=:=
:=:=:=if(bit_test(address,0)) { // test if this is a read operation
:=:=:=
:=:=:=i2c_write(0b10101010); // send byte back to master
:=:=:=
:=:=:=}
:=:=One obvious big problem here, is time...
:=:=Remember that the I2C interrupt, with data available, will occur once the last bit of the data is seen. Even at 20MHz, there will be a latency of a few uSec, before you actually get to the handler code, and this itself will take a few uSec more to execute. Then the main code, presumably contains other routines, in the loop, waiting to look at the address. The data is only finally written back to the I2C port, once all these times have executed. Your master routine, starts reading the returned data immediately. Unfortunately, I would suspect, that the data is not actually ready yet...
:=:=I had similar problems with SPI, and it was salutory, to work out the 'worst case' response times (where the second chip, was doing other things like servicing serial interrupts as well).
:=:=I think I'd stick the return into the interrupt service routine if possible (have a buffer containing the data to be sent?), and add a delay between sending the data, and looking for the return, and see if things improve.
:=:=However I think the reason for your problem, may be that the 'address' byte, in the buffer, doesn't have the low bit set as you expect!.
:=:=I'm not quite sure how this behaves (I'm really an SPI man, rather than slave I2C), but if you read the chips data sheet, it comments that the status of the direction bit, gets copied into the R/W bit of the SSPSTAT register, and the received address, gets transferred to the receive buffer. Now it is possible that the address in this context, is the seven bit value used for address matching, and not the eight bit data...
:=:=The other comment, is that I'd be very 'careful' about using the CCS code inside the IRQ. Instead I'd go 'DIY' in the handler, with something like:
:=:=#bit DIRN=0x94,2
:=:=#byte SSPBUF=0x13
:=:=#bit BUFFERFULL=0x94,0
:=:=#bit OVERFLOW=0x14,6
:=:=#INT_SSP
:=:=void ssp_interupt () {
:=:=//Technically if you arrrive here, a byte _is_ waiting, hence
:=:=//there is no need to poll the chip
:=:= if (OVERFLOW) OVERFLOW=0;
:=:= if (i==0) {
:=:= if (DIRN) {
:=:= address=SSPBUF;
:=:= SSPBUF=0b10101010;
:=:= }
:=:= }
:=:= else if (i==1)
:=:= data=SSPBUF;
:=:= else if (i==2)
:=:= data2=SSPBUF;
:=:= i++;
:=:=}
:=:=
:=
:=OK, but I know the ISR is being called since I added a line that switches LEDs on at the beginning of the ISR. Now, if, as you said, there is no need to poll the device, why does i2c_poll() fail? I know this happens because I change the LEDs output inside the if(i2c_poll()){ /* ........ */ }.
:=
:=Also, I am able to write to the slave easily, using the CCS functions inside the slave's ISR! This is why I think the slave is not recognizing it is being addressed when you change the address byte to 0x01 from 0x00. Does that make sense? Will the ISR be called even if the address sent doe NOT match the slave's addresS?
:=
This ties up with the _time_ problem. Remember that once you are inside the ISR, it takes nearly as long to get out of it again (the chip has to restore the registers saved entering the routine). Also 'writing to the slave', there is an entire byte time between the first character, and the next. With the 'read from slave', there is no time at all allowed for the slave to get the return data into the buffer before the master starts requesting it. This is why I raised this as the first (and I think most likely), major problem...
The chip should allways interrupt on your byte, since it matches the hardware general call address. This should happen, even if your address was set to something different. To handle this, the interrupt code, would have to check more deeply, what has been sent.
Your would get an interrupt on _any_ byte, if the overflow flag has been set. With this, the data would probably be invalid, and the interrupt would occur just once, unless the overflow condition is cleared. It'd be very interesting to add a test at the start of the code for the overflow flag, and change an LED if this is set. It may be that the chip is 'seeing' garbage.

:=I will try your DYI routine and see if that works...

Even with this, it'd be worth adding a 'delay_us(10)', between sending the address, and trying to read the return. I think the 'heart' of the problem is timing.

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516068
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