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 Slave firmware - problem debugging
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

I2C Slave firmware - problem debugging
PostPosted: Tue Jan 13, 2004 6:17 am     Reply with quote

Hi all, I have written an I2C slave handler in the form of a state machine within the SSP interrupt. The code is based on Microchip app note AN734, but I wrote it in CCS. I have a test 16C84 sending out data in the form: "Slave address(write) - AA - 55" then: "Slave address(read) - byte - byte". My slave (on a 16F818) is acknowledging the write transaction, but on the read transaction, the overflow (SSPOV) bit is getting set and consequently nothing is being acknowledged. The SDA line floats, so my master reads back 'FFFF' This then screws up further write transactions.

The overflow bit is not mentioned in the datasheet with regard to a slave transmission, only for reception. Has anyone written a handler in a similar way and had this problem?

Also, is it such a good idea to have the entire handler inside the interrupt as it it in AN734? Is there any other way of doing it, except for just polling which I can't do.
Mark



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

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

PostPosted: Tue Jan 13, 2004 7:11 am     Reply with quote

I have a slave handler that works just fine. Upon the read transaction the clock should be held low by the hardware. When the isr fires, you have to load the read value into the buffer and the release the clock. Post your code. It is kinda hard to tell you where the problem is without seeing what you are doing.
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

my code
PostPosted: Tue Jan 13, 2004 7:29 am     Reply with quote

Hi Mark, here is my handler....

int RX_buff[16],in_ptr=0,out_ptr=0,read_ptr=0,stat_compare;

#byte SSPBUF=0x13 // I²C control & status register defines.
#byte SSPCON=0x14
#byte SSPADD=0x93
#byte SSPSTAT=0x94

#bit SSPOV = 0x14.6 // I²C status bits.
#bit WCOL = 0x14.7
#bit CKP = 0x14.4
#bit BF = 0x94.0

void init_i2c(){
SSPCON = 0x36;
SSPADD = SLAVE_ADDRESS; // Set to 0x2E.
SSPSTAT = 0;
WCOL = 0;
SSPOV = 0;
}

void write_i2c(){
SSPOV=0;
while(BF); // If buffer is full, wait to clear.
do{
WCOL=0; // Clear a previous collision flag.
SSPBUF=RX_buff[out_ptr]; // Load buffer and check again for collision.
}while(WCOL);
out_ptr++;
out_ptr &= 15;
CKP=1; // Release clock to initiate transmit.
}

#int_ssp
I2C_ISR(){ //I2C State Machine/ISR
DATA^=1; // Toggle an LED on each ISR to show activity.

stat_compare = (SSPSTAT & 0b00101101); // Mask out "don't cares".
switch(stat_compare){ // Invoke state machine.

case 0b00001001: // Write mode (Master to Slave), last byte was address.
#asm // Using ASM to save a Tcy!
bcf 0x03.5;
movf SSPBUF,W; // Do a dummy read to clear BF.
#endasm
in_ptr=0;
out_ptr=0;
break;

case 0b00101001: // Write mode (Master to Slave), last byte was data.
rx_buff[in_ptr++]=SSPBUF; // Read bytes into buffer.
in_ptr &= 15; // Roll around buffer pointer, keeping buffer circular
break;

case 0b00001100: // Read mode (Slave to Master), last byte was address.
out_ptr=0;
write_i2c();
break;

case 0b00101100: // Read mode (Slave to Master), last byte was data.
write_i2c();
break;

case 0b00101000:
// Master sent a 'NACK'. Module reset. No action necessary.
break;

default:
COLL ^= 1; // Put ERROR LED on! Something went spam up!!
break;
}
}
Guest








PostPosted: Tue Jan 13, 2004 11:05 pm     Reply with quote

Neil,
I recently updated an old post that may be of assistance.
http://www.ccsinfo.com/forum/viewtopic.php?t=14473&

I found that if the master was using hardware i2c there was a timing problem, so now the CKP bit is used on the slave (clock stretching) as a form of handshaking to keep the master waiting while the slave completes each state. Previously, I was only doing this in state4 as a test of clock stretching.
Also, small delays have been added to the master to allow the slave time to get to/from the ssp interrupt handler.
HTH
Mark



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

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

PostPosted: Wed Jan 14, 2004 7:53 am     Reply with quote

Hi Neil,

Try this code out. I didn't compile it so there might be some syntax errors. Check out the way you are signalling the error LED. It looks as though you are toggling it. I think that you want it on for an error and not when there is not.

Code:

#define OVERFLOW_BIT   6
#define STOP_BIT       4
#define READ_WRITE     2
#define CKP            4
#define BF_BIT         0
#define ADDRESS_BIT    5

int RX_buff[16],in_ptr=0,out_ptr=0,read_ptr=0,stat_compare;

#byte SSPBUF=0x13 // I²C control & status register defines.
#byte SSPCON=0x14
#byte SSPADD=0x93
#byte SSPSTAT=0x94

#bit SSPOV = 0x14.6 // I²C status bits.
#bit WCOL = 0x14.7
#bit CKP = 0x14.4
#bit BF = 0x94.0

void init_i2c()
{
  SSPCON = 0x36;
  SSPADD = SLAVE_ADDRESS; // Set to 0x2E.
  SSPSTAT = 0;
  WCOL = 0;
  SSPOV = 0;
}


#int_ssp
I2C_ISR()
{
 
  DATA^=1; // Toggle an LED on each ISR to show activity.
 
  if (bit_test(SSPCON,OVERFLOW_BIT))
  {
    // dummy read to clear the buffer
    #asm // Using ASM to save a Tcy!
      bcf 0x03.5
      movf SSPBUF,W  // Do a dummy read to clear BF.
    #endasm
    bit_clear(SSPCON,OVERFLOW_BIT);
    COLL ^= 1; // Put ERROR LED on! Something went spam up!!
   return;
  }

  if (bit_test(SSPSTAT,STOP_BIT))         
  {
    // We received a stop so we are done
    return;
  }
  else         
  {
    // see if were we addressed in read mode
    if (bit_test(SSPSTAT, READ_WRITE))
    {
      SSPBUF=RX_buff[out_ptr]; // Load buffer and check again for collision.
      out_ptr++;
      out_ptr &= 15;
      bit_set(SSPCON, CKP);
    }
    // See if we have data
    else if (bit_test(SSPSTAT,BF_BIT))
    {
      // See if we were addressed
      if (!bit_test(SSPSTAT,ADDRESS_BIT))
      {
       // dummy read to clear the buffer
        #asm // Using ASM to save a Tcy!
          bcf 0x03.5
          movf SSPBUF,W  // Do a dummy read to clear BF.
        #endasm
        in_ptr=0;
        out_ptr=0;
      }
      else
      {
        rx_buff[in_ptr++]=SSPBUF; // Read bytes into buffer.
        in_ptr &= 15; // Roll around buffer pointer, keeping buffer circular
      }
    }
  }
}     
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Thanks Mark
PostPosted: Wed Jan 14, 2004 9:51 am     Reply with quote

Hi Mark, yes I am toggling the LED. It is being toggled on every other interrupt (seen as a blip on the DATA LED), meaning that (I think) Master to slave transactions are OK but slave to master is not. Looking at the scope the master write is being acknowledged, but slave write has something wrong, seen as all high, with NACK on every 9th clock.

Thanks for the code anyway, very different to mine and Kenny's approach. Perhaps more elegant. I will give it a try, but would rather get my own working as I can't be that far off! I had no idea there would be so many pitfalls with I²C!

Neil.
Mark



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

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

PostPosted: Wed Jan 14, 2004 10:08 am     Reply with quote

Post the snippet of the transmiter. The code I posted I believe to be a little more "forgiving" and yours is a bit more "strict". You should be sending the following:

start condition
write slave address
write 'AA'
write '55'
start condition
write slave address | 1
read byte w/ ack
read byte without ack
stop condition

If you were to send a stop before the read part of the sequence, you can get the problem that you are seeing. Is this the problem? The code you posted checks for the start bit in every sequence. I couldn't use this approach in my code because the start condition is not alway valid. I use master and slave and switching between the two causes the bit to become invalid for until the next start condition. The same holds true for the stop bit.
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Ah-Ha!
PostPosted: Wed Jan 14, 2004 10:56 am     Reply with quote

Mark, that is exactly what I am doing. I am sending:
    start condition
    write slave address
    write 'AA'
    write '55'
    stop condition

    start condition
    write slave address | 1
    read byte w/ ack
    read byte without ack
    stop condition


Are you saying that the above would cause the problem, or only if I did it the way you listed?

BTW. I am using S/W I²C from a 16C84 as the master. The SCL clock speed is about 30kHz, xtal speed 4.0MHz. Is there a feature of s/w mode which would add to the problem?

Cheers,
Neil.
Mark



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

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

PostPosted: Wed Jan 14, 2004 12:23 pm     Reply with quote

There should not be a stop before the second start condition. The second start is known as a repeated start. It is termed this because 2 starts are generated without a stop between them. Now I can see it possible that the stop bit is received before the isr has time to respond in which case your case statements that are checking for a start condition would not get executed.
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Making more sense now.
PostPosted: Thu Jan 15, 2004 5:47 am     Reply with quote

Very Happy Hi Mark,

I just re-wrote my master code (I lost the last bit of code) and put in the repeated start instead of the stop/start that I had. I haven't made ANY changes to the slave code (yet!)

With these changes, the error LED never comes on, the interrupt triggers on every byte sequence and the slave transmit is receiving the correct ack/not ack from the master.

My slave seems to be sending the wrong data back to the master rather than echoing back what it just received, but I think that is a coding problem with my buffer usage, and easily solved.

So, thanks, that seems to have solved the problem.
If you want to look, here is my master test program:

Code:
#include <16F84A.h>
#use delay(clock=4000000)
#fuses NOWDT,XT,PUT,NOPROTECT
#use rs232(baud=9600,parity=N,xmit=PIN_A3) // No RX pin used!
#use I2C(master, sda=PIN_A0, scl=PIN_A1)
#bit trigger=6.7     // PortB,7 going to a switch to trigger the transaction.
long readback;
#define hi(x) *(&x+1)

void main() {
   setup_counters(RTCC_INTERNAL,RTCC_DIV_1);
   set_tris_A(0b00000011);
   set_tris_B(0b10000000);
   while(TRUE){
      while(trigger){
         i2c_start();
         i2c_write(0x2E);
         i2c_write(0xAA);
         i2c_write(0x55);
         
         i2c_start();      // Repeated start.
         i2c_write(0x2F);
         readback = i2c_read();
         hi(readback) = i2c_read(0);
         i2c_stop();
         
         printf("%4X\n\r",readback);
      }
   }
}

Thanks. I'd be lost without this forum. Sad but true (In the words of Metallica! Twisted Evil )

Neil.
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Question for Mark
PostPosted: Tue Jan 20, 2004 8:09 am     Reply with quote

Hi Mark,
I figured you would be best to answer this as you helped with the code for this slave handler. I now find that both your code and my code (which are both quite different) give the same behaviour now that my master is sending a repeated start. It works fine running on a 16F877 at 8MHz, but on my final target device (16F818 @ 4MHz) it gives all sorts of reply values back to the master. The reply transaction (Slave -> Master) appears to be cut short at only 2.5 bytes length! Do you know off hand whether 4MHz is actually fast enough when using the SSP interrupt like this. Note, I also have the CCP and EXT ints running to drive thyristors as a phase angle controller. (Yep, still on THAT project!!)

Regards,
Neil.
Mark



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

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

PostPosted: Tue Jan 20, 2004 5:58 pm     Reply with quote

4MHz is fast enough. Our first modules used 4MHz devices and the later 20MHz. They talk to fine to each other. What do you mean by 2.5 bytes? Are you looking at the signal with a scope? Your master code only reads 2 bytes but from your post it sounds as though you are expecting more.
Mark



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

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

PostPosted: Tue Jan 20, 2004 6:01 pm     Reply with quote

Take a look at the errata for that device. It will probably explain your problems
Quote:

In its current implementation, the module may fail
to correctly recognize certain Repeated START
conditions. For this discussion, a Repeated
START is defined as a START condition presented
to the bus after an initial valid START condition has
been recognized and the START status bit
(SSPSTAT<3>) has been set, and before a valid
STOP condition is received.
If a Repeated START is not recognized, a loss of
synchronization between the Master and Slave
may occur; the condition may continue until the
module is reset. A NACK condition, generated by
the Slave for any reason, will not reset the module.


http://www.microchip.com/download/lit/suppdoc/errata/80132c.pdf
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

2.5 Bytes
PostPosted: Wed Jan 21, 2004 4:55 am     Reply with quote

Hi Mark, I should have explained that better. I even got 2.5 wrong (had been a frustrating day!) What I meant was I am looking at the entire transaction on a scope. I have cut the master down to just sending <S><address> <data byte> <S> <Address|1> <data_read> <P> so the entire read/write transaction consists of 4 bytes (incl. the 9th scl). I was seeing 3bytes + 8 clock cycles on the scope, meaning that the NACK clock cycle was missing.
I tried using a hardware master, instead of bit-banging and this produced one whole transaction, without ACKs and then the SDA line remained low, preventing any further transaction taking place. I found this to be the slave holding the data line low.

I have captured these on the scope and exported them as excel graphs, but I don't know how to post them on this forum. I don't have access to web space. I could email them if you have time?
I think I have got too bogged down with this situation and need to take a step back. Surely it can't be this difficult?!

Thanks again,
Neil.
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

SSP module errata
PostPosted: Wed Jan 21, 2004 5:03 am     Reply with quote

Hi Mark, just looked at the errata and it does appear that the (or a) problem could be the repeated start.
Quote:
A Repeated START occurs within the frame
of a data or address byte. The unexpected
START condition may be erroneously interpreted
as a data bit, provided that the
required conditions for setup and hold times
are met.
Maybe I should put in a short delay after sending the last byte to segregate the byte and the start?

Sorry to be directing all these messages at you! I hope you don't mind. Please tell me if you do, I don't want to be robbing your time!!
Regards,
Neil.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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