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 slave writing troubles....

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



Joined: 03 Oct 2003
Posts: 13

View user's profile Send private message

I2C slave writing troubles....
PostPosted: Fri Oct 03, 2003 3:19 pm     Reply with quote

I have been trying to write a simple I2C master/slave communications program, and I am having all kinds of difficulties.

Both are PIC18F252's. I am on CCS PCH 3.155.
I am using the hardware I2C, with 1k pullups.

I have some 24LC65's on the same bus. I can talk to them fine. I am pretty sure it is my slave code that is the problem, but I cannot isolate it.

The behavior I see is that I never receive the 'R' from the master. I always miss it. It's like some kind of timing problem.

Any help would be greatly appreciated.

Thanks,

- Ted


Here is my slave:
--------------------

#use I2C(slave,sda=I2C_SDA,scl=I2C_SCL, address=SLAVE_ADDR,FORCE_HW)

#INT_SSP
void ssp_interupt ()
{
static int lsb=0;
static int msb=0;
int cmd;
int readbyte=0;


if(!i2c_poll())
{
if(icstate==1)
{
i2c_write(msb);
i2c_write(lsb);
msb=lsb=0;
}
icstate=0;
}
else
{
readbyte = i2c_read();
switch(icstate)
{
case 0:
icstate=1;
return;
case 1:
cmd = readbyte;
if(cmd=='R')
{
fprintf(DEBUG, "YES!!!\r\n");
msb = 3;
lsb = 3;
}
else
msb=lsb=99;

icstate=0;
return;
}
}
}

Here is my master:
-----------------------

#use I2C(master, sda=I2C_SDA, scl=I2C_SCL, FORCE_HW)

i2c_start();
i2c_write(MOTORBOARD);
fprintf(DEBUG, "Sending command\r\n");
i2c_write('R');
i2c_start();
i2c_write(MOTORBOARD+1);
msb = i2c_read(1);
lsb = i2c_read(0);
i2c_stop();
fprintf(DEBUG, "Motorboard said (%D)(%D)\r\n", msb, lsb);
tedlarson



Joined: 03 Oct 2003
Posts: 13

View user's profile Send private message

PostPosted: Sat Oct 04, 2003 5:08 pm     Reply with quote

I am really starting to get frustrated....

After fiddling with this problem for 2 days straight now....I am getting the feeling that the #use i2c stuff in the compiler doesn't work properly for building a slave that is timed properly. I went back and read MANY posts on this message board where everyone solved the problem by just taking control of the registers and DIYing a I2C slave.

All I want to do is build an I2C slave that can read 4 sequential bytes from a master, and send 4 sequential bytes in response. This sounds so simple....but it apparently isn't . The ex_slave.c example makes it look like it should be easy....unfortunately it isn't.

Does someone have a slave example USING the #use I2c that actually works for more than 1 byte up and 1 byte back that they could post?

Thanks,

- Ted
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Sun Oct 05, 2003 8:48 am     Reply with quote

Well I posted something on the old board:

http://www.ccsinfo.com/forum/viewtopic.php?t=6821&highlight=exslave+c

The first example is doing it yourself using the registers (the way I would do it). The second example I modified the ex_slave.c file.

For your example, did you try an remove the fprintf statement from the slave code. That will cause a delay and probably a failure.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Sun Oct 05, 2003 8:54 am     Reply with quote

One other thing, does SLAVE_ADDR == MOTORBOARD? If not, that will also be a problem.
tedlarson



Joined: 03 Oct 2003
Posts: 13

View user's profile Send private message

PostPosted: Wed Oct 08, 2003 9:55 am     Reply with quote

Mark wrote:
One other thing, does SLAVE_ADDR == MOTORBOARD? If not, that will also be a problem.


Yes...they are the same. Thanks for the reply!

After LOTs of fiddling with the ex_slave.c code, I was able to get the slave to respond with several bytes. However, you cannot just do a write, write, write to reply...The writing appears to NOT be synchronous. So, it is possible to send all the bytes faster than the receiver can receive the, and then you end up overrunning the master.

So...for example:
if (i2c_poll() == FALSE) {
if (fState == ADDRESS_READ) { //i2c_poll() returns false on the
i2c_write(1);
i2c_write(2);
i2c_write(3);
fState = NOTHING
}
}

Will NOT work. But, if you put delays (ugh) between all the writes...it will. It may be that the example you came up with, with the while loop writing from an array, has just enough delay in the loop, that it works.

My biggest problem is the receiving side of the slave. If I want to receive several bytes....for example:

...ex_slave.c...
...
else if (fState == CONTROL_READ) {
address = incoming;
fState = ADDRESS_READ;
}
else if (fState == ADDRESS_READ) {
buffer[address] = incoming;
a = i2c_read();
b = i2c_read();
c = i2c_read();
fState = NOTHING;
}
}

This will not work. Again...it is a timing problem between the master and the slave. After ALOT of trial and error, I have never been able to get the timing of this part to work properly.

I am going to try to take your "take direct control of registers" example and adapt it to what I am trying to do. I'll post my results.

Again....I am so frustrated that something that should be simple via the built in libraries, is so darn difficult. Timing problems are always the biggest PITA's to debug.

Thanks,

- Ted
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Wed Oct 08, 2003 10:57 am     Reply with quote

Quote:

This will not work. Again...it is a timing problem between the master and the slave. After ALOT of trial and error, I have never been able to get the timing of this part to work properly.


It is not really a timing issue. You will get an interrupt each time a byte is transmitted. You should only read 1 byte per interrupt. Think of this as you would RS232 data coming in from a serial port. The difference is that there are many flags that are set to indicate why the int was invoked (start, stop, data or address, read or write mode, etc.. )so that you can take the approiate action. Read the datasheet on the PIC and I believe it will be very clear to you. I wouldn't use the CCS functions. They are okay for some but not in all cases.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Wed Oct 08, 2003 10:59 am     Reply with quote

Because the master triggers everything through the clock all delays between bytes should be in the master. When I do syncronous transfers using SPI the master over runs the slave if I dont place delays between bytes.

In the slave
Code:

   SPI_Buffer[0] = spi_read(SPI_Buffer[0]);
   SPI_Buffer[1] = spi_read(SPI_Buffer[1]);
   SPI_Buffer[2] = spi_read(SPI_Buffer[2]);
   SPI_Buffer[3] = spi_read(SPI_Buffer[3]);

In the master
Code:

      SPI_Buffer[0] = spi_read(SPI_Buffer[0]); delay_cycles( 8 );
      SPI_Buffer[1] = spi_read(SPI_Buffer[1]); delay_cycles( 8 );
      SPI_Buffer[2] = spi_read(SPI_Buffer[2]); delay_cycles( 8 );
      SPI_Buffer[3] = spi_read(SPI_Buffer[3]); delay_cycles( 8 );


If I remove the delay it stops working. Keep in mind that this is using direct addressing for all of these values. Also the interupts are disabled during the transfer. The clock is 2Mhz and baud is 1M baud in and out. At 8uS for data and 4uS pause per byte. The interupt flag in the master that signals the completion of clocking out data byte occures before the flag for recieved data byte in the slave(I guess). This would allow concurent loading of the shift register in the master without any pauses due to loading/unloading process time. In order to allow a good interupt driven transfer the hardware I2C and SPI should have been double buffered much like the USART has been.
tedlarson



Joined: 03 Oct 2003
Posts: 13

View user's profile Send private message

PostPosted: Wed Oct 08, 2003 12:09 pm     Reply with quote

CCS tech support just sent me the following code.
It works very well. My problem is now solved. You can send 2 bytes up, and get 2 bytes back, with a 1 byte address...although it would be easy to extend to 2. I have this running between two PIC18F252's running at 40mhz and it seems solid.

Enjoy!

- Ted

<PRE>
typedef enum {NOTHING, CONTROL_READ,
ADDRESS_READ, READ_COMMAND_READ, DATA_READ_LOW, ADDRESS_READ_LOW} I2C_STATE;

I2C_STATE fState;
byte address;
long buffer[0x10]={0x103, 0x205, 0x307, 0x409, 0x50A, 0x60C, 0x710, 0x812, 0x914, 0xA16};

#INT_SSP
void ssp_interupt ()
{
byte incoming;
static byte dl, dh;

if (i2c_poll() == FALSE) {
if (fState == ADDRESS_READ) { //i2c_poll() returns false on the
i2c_write(make8(buffer[address], 1));//interupt receiving the second
fState = ADDRESS_READ_LOW; //command byte for random read operation
}
else if (fState == ADDRESS_READ_LOW) { //i2c_poll() returns false on the
i2c_write(make8(buffer[address], 0));
fState = NOTHING; //command byte for random read operation
}
}
else {
incoming = i2c_read();

if (fState == NOTHING){
fState = CONTROL_READ;
}
else if (fState == CONTROL_READ) {
address = incoming;
fState = ADDRESS_READ;
}
else if (fState == ADDRESS_READ) {
dh = incoming;
fState = DATA_READ_LOW;
}
else if (fState == DATA_READ_LOW) {
dl = incoming;
buffer[address] = make16(dh,dl);
fState = NOTHING;
}
}
}
</PRE>
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Wed Oct 08, 2003 12:15 pm     Reply with quote

Do a test where you send only one byte and then stop (simulating some sort of error). Then try and do a correct sequence. I believe that you will find their state machine gets trashed! That is why it is better to look at the flags when reading the data.
tedlarson



Joined: 03 Oct 2003
Posts: 13

View user's profile Send private message

PostPosted: Wed Oct 08, 2003 12:47 pm     Reply with quote

Mark wrote:
Do a test where you send only one byte and then stop (simulating some sort of error). Then try and do a correct sequence. I believe that you will find their state machine gets trashed! That is why it is better to look at the flags when reading the data.


I totally agree with you. Except...I couldn't get any of the code which does flag reading to work at all. I have attached it below. I just get garbage in and out. The reads always return 0xff's.

Thanks,

-Ted

<PRE>

SLAVE CODE
---------------

#byte SSPADD = 0x93 // Slave address held here
#byte SSPCON1= 0x14 // SSP control reg 1
#byte SSPCON2= 0x91 // SSP control reg 2
#byte SSPBUF = 0x13 // Buffer for SSP data
#byte SSPSTAT= 0x94 // SSP status reg

#bit BF = SSPSTAT.0 // Buffer full status bit. Read-only bit
// Receive mode 1 = SSPBUF full.
// Tx mode 1 = data transmit in progress, SSPBUF full.
// 0 = complete, SSPBUF empty.
// (does not include ack and stop bits in either case)
#bit R_W = SSPSTAT.2 // Holds read/write status from last address match, valid
// to next start,stop, or not /ack bit.
// Slave mode 1 = read, Master mode transmit in progress.
#bit START= SSPSTAT.3 // Start bit has been detected last
#bit STOP = SSPSTAT.4 // Stop bit has been detected last
#bit D_A = SSPSTAT.5 // Data/addr bit. 1 = last byte received or transmitted
// was data.
#bit CKE = SSPSTAT.6 // 0 = input levels are i2c specs, 1 = SMB specs.
#bit SMP = SSPSTAT.7 // Slew rate control. 1 = disabled for 100kHz or 1MHz modes

// Slave mode 7 bits //
#bit SSPM0 = SSPCON1.0 // SSP mode bits 0
#bit SSPM1 = SSPCON1.1 // " 1
#bit SSPM2 = SSPCON1.2 // " 1
#bit SSPM3 = SSPCON1.3 // " 0
#bit CKP = SSPCON1.4 // Clock, 1 = enable clock, 0 = clock stretch
#bit SSPEN = SSPCON1.5 // SSP enable bit
#bit SSPOV = SSPCON1.6 // Receive overflow indicator bit. In receive mode, a
// byte is received while the SSPBUF is holding
// previous byte. Must clear in software.
#bit WCOL = SSPCON1.7 // Write collision detect bit. In slave mode, 1 means
// SSBUF written while transmitting previous word.
// Must clear in software.

// Bits 0 to 6 of SSPCON2 are for Master only //
#bit SEN = SSPCON2.0 // Initiate Start on SDA & SCL, hardware clears
#bit RSEN = SSPCON2.1 // Initiate repeated start, hardware clears
#bit PEN = SSPCON2.2 // Initiate Stop on SCA & SCL, hardware clears
#bit RCEN = SSPCON2.3 // Enables i2c receive mode
#bit ACKEN = SSPCON2.4 // Initiate Ack on SDA & SCL, xmit ACKDT bit, h/w clears
#bit ACKDT = SSPCON2.5 // Master receive mode only. Acknowledge bit, value
// sent when user initiates ack sequence after receive
#bit ACKSTAT= SSPCON2.6 // Master transmit mode, 1 = ack not received from slave
#bit GCEN = SSPCON2.7 // General call enable bit (slave mode only)


#define BUF_SIZE 16
int sbuf[BUF_SIZE]; // Buffer for i2c data to be received or transmitted
int sindex;
int rbuf[BUF_SIZE];
int rindex;
int addrbyte;

// Possible i2c states, SSPSTAT for the four relevant bits //
#define STATE1 0x09 // D/A=0,S=1,R/W=0,BF=1. Master tx, slave addr
#define STATE2 0x29 // D/A=1,S=1,R/W=0,BF=1. Master tx, data
#define STATE3 0x0C // D/A=0,S=1,R/W=1,BF=0. Master rx, slave addr+1
#define STATE4 0x2C // D/A=1,S=1,R/W=1,BF=0. Master rx, data with /ack
#define STATE5 0x28 // D/A=1,S=1,R/W=0,BF=0. Master rx, data with not /ack

//SLAVE INTERRUPT HANDLER..............
#INT_SSP
interrupt_ssp()
{
switch(SSPSTAT & 0x2D)
{ // Keep only bits D/A, S, R/W, BF
case STATE1 :
// Master write operation, address byte in SSPBUF
// Clear the data buffer
//for(rindex=0;rindex < 16; rindex++)
//rbuf[rindex]=0;

rindex = 0; // Reset the data buffer index
SSPOV = 0; // Clear address overflow flag, could be set from last
// time because after not /ack, SSPBUF was still full.
addrbyte = SSPBUF; // Dummy read to clear BF (read only bit)
break;
case STATE2 :
// Master write operation, data byte in SSPBUF
// Get data byte (also clears BF)
SSPOV = 0;
rbuf[rindex++]=SSPBUF;
break;
case STATE3 :
// Master has begun new read operation by initiating a START or RESTART
// then sending Slave address (read) byte (now in SSPBUF).
// Looks like clock enable bit CKP cleared on interrupt, so must
// set it again to allow Master to clock data byte out
// SCL held low, Master can be kept waiting here if necessary
sindex = 0; // Reset buffer index
SSPBUF = sbuf[sindex++]; // Load 1st byte from data buffer
CKP = 1; // Enable SCL for Master to shift byte out
break;
case STATE4 :
// Master read operation, last byte was data, SSPBUF empty
// Move next byte to SSPBUF and SSPSR
// Same comment for CKP bit as in STATE3
SSPBUF = sbuf[sindex++]; // Get next byte from data buffer
CKP = 1; // Enable SCL for Master to shift byte out
break;
case STATE5 :
// A not /ack (high) was received from Master in response to data
// byte received from Slave (last byte). Slave i2c logic is reset,
// and waits for next Master operation.
break;
default:
// Error, trap here. Watchdog will reset pic (must be enabled, and
//watchdog timer cleared in other loops)
//while(1);
break;
}
}


icbus_init()
{
sbuf[0]=3;
sbuf[1]=4;
sbuf[2]=5;

SSPCON1 = 0x36; // i2c slave mode, 7-bit address
SSPADD = SLAVE_ADDR; // Set slave address
SMP=1; //SLOW

enable_interrupts(INT_SSP);
}

main()
{

while(1)
{
delay_ms(100);
fprintf(DEBUG, "(%x)(%x)(%x)\r\n", rbuf[0], rbuf[1], rbuf[2]);

}
}

MASTER CODE
-----------------

i2c_start();
i2c_write(MOTORBOARD);
i2c_write(1);
i2c_write(2);
i2c_write(3);
i2c_write(MOTORBOARD+1);
a = i2c_read();
b = i2c_read();
c = i2c_read();
d = i2c_read(0);
i2c_stop();
fprintf(DEBUG, "Result is (%x)(%x)(%x)(%x)\r\n", a, b, c, d);

</PRE>
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