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

[partly solved] 2C slave example. Read consecutive bytes.

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



Joined: 27 Dec 2004
Posts: 21

View user's profile Send private message

[partly solved] 2C slave example. Read consecutive bytes.
PostPosted: Fri Mar 22, 2013 9:50 pm     Reply with quote

In the example code for I2C slave, only one byte is read per I2C frame.
ex [start][ChipAddr write][write address][start][ChipAddr read][read byte 1][stop]


Code:
#INT_SSP
void ssp_interupt ()
{
   BYTE incoming, state;

   state = i2c_isr_state();
   
   if(state <= 0x80)                     //Master is sending data
   {
      incoming = i2c_read();
      if(state == 1)                     //First received byte is address
         address = incoming;
      if(state == 2)                     //Second received byte is data
         buffer[address] = incoming;
   }
   if(state == 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[address]);
   }
}


This appears to contradict the example in the documentation where for a 0x80, we do nothing.

Code:
#INT_SSP
   void i2c_isr()
{
      state = i2c_isr_state();
      if((state== 0 ) || (state== 0x80))
        i@c_read();
      if(state >= 0x80)
         i2c_write(send_buffer[state - 0x80]);
      else if(state > 0)
         rcv_buffer[state - 1] = i2c_read();
}






How do you read more than one byte?
ex [start][ChipAddr write][write address][start][ChipAddr read][read byte 1][read byte 2].....[stop]
It is my understanding that i2c_isr_state() will return a number beginning at 0x80 on writes and increment each byte.
Will this work?

Code:
#INT_SSP
void ssp_interupt ()
{
   BYTE incoming, state;

   state = i2c_isr_state();
   
   if(state ==0)                     //address byte
   {                                     // we do nothing
   }
   else if (state == 1)                     //First received byte is address
   {
        address = i2c_read();
    }
    else  if (state < 0x80)  //Second or more received byte is data we write to the buffer
         buffer[address] = i2c_read();
   }
   else if (state == 80) // match the address for a write
   {    // do nothing
   }
    else if (state>80)  //a value above 0x80 is request for a read.
   {
      i2c_write(buffer[address++]);
   }
}



Also, is there a way to be notified or interrupted once we receive a STOP?[/code]


Last edited by jmann on Mon Apr 01, 2013 9:28 pm; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Mar 22, 2013 10:24 pm     Reply with quote

This post has sample code to read 8 bytes from an i2c slave PIC:
http://www.ccsinfo.com/forum/viewtopic.php?t=39565&start=4
Ttelmah



Joined: 11 Mar 2010
Posts: 19326

View user's profile Send private message

PostPosted: Sat Mar 23, 2013 1:38 am     Reply with quote

There is a slight misunderstanding here. There is not an 'I2C_frame' as far as the interrupt is concerned. The interrupt occurs for every I2C _byte_ transaction.

It can also occur for start/stop (this is a programmable option normally left off).
Then it next occurs on the start byte.
Then it occurs for every byte in the transaction, one after the other.

The thing keeping track of the 'frame', is i2c_isr_state, and 'address'. Now in I2C, there are two 'addresses'. The first is the device address, then you have the 'register address'. The ISR, responds to the device address, with isr_state==0, The next transaction normally sends the register address, and this is 'state==1', and is put into the 'address' variable.
Then the write transaction puts the next byte(s) into the location defined by this. The only change needed to handle more bytes, is to have the i2c_write increment the 'register address'.
Code:

   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[address++]);
   }

Then on each call the next byte will be sent.
This is the so called 'auto-increment' mode for an I2C slave.
This is what the example PCM points to, does.

Best Wishes
jmann



Joined: 27 Dec 2004
Posts: 21

View user's profile Send private message

PostPosted: Sat Mar 23, 2013 6:56 am     Reply with quote

Ttelmah wrote:

Code:

   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[address++]);
   }



picture of a frame: http://www.8051projects.net/i2c-twi-tutorial/read-i2c.png
So, I see, this is how we're handling it with the built-in functions.
Code:
[start]

[write ChipAddr writeFlag] <<-- interrupt  with i2c_isr_state == 0x00
do nothing

[write RegisterAddress] <<-- interrupt with i2c_isr_state == 0x01
i2c_read(RegisterAddress)

[start] <<-- this is the second start for a i2c frame with master writing data

[write ChipAddr readflag] <<-- wouldn't the i2c_isr_state read in this interrupt be 0x80?
i2c_write(register[RegisterAddress] <<--  we need to pre-load the data to be sent, Byte 1
this byte will be read out of the i2c register and then we'll get another interrupt when it is empty.

[read byte 1] <<-- this is read out of the I2C buffer, and then we get an interrupt because the buffer is empty with i2c_isr_state == 0x81
i2c_write(register[RegisterAddress+1] <<--  we need to pre-load the data to be sent, byte 2

[read byte 2]  <<-- this is read out of the I2C buffer, and then we get an interrupt because the buffer is empty with i2c_isr_state == 0x82
i2c_write(register[RegisterAddress+2] <<--  we need to pre-load the data to be sent

.....

[read byte N]  <<-- this is read out of the I2C buffer, but there isn't an ACK, so the interrupt doesn't get called to load the next byte

[stop]


That last byte is read by the master without sending an ack. Then we get a stop.

This brings me to the next question: Let's say I was waiting for the frame to be completed before processing the data sent to me. The point is that I want to wait for a STOP. Are there any built-in functions that will let me get an interrupt when there is a STOP so I can can know when I am done getting data so I can process it?
jeremiah



Joined: 20 Jul 2010
Posts: 1321

View user's profile Send private message

PostPosted: Sat Mar 23, 2013 7:36 am     Reply with quote

I don't think there is a built in to see the stop bit. Most I2C devices don't need it as the protocol is either defined as a specific message or there is a length field as part of the message (only really needed for master -> slave data as the master can ack/nack slave->master data).

However, if you absolutely need to see it, then you can use #bit to access it directly and see what the value is.

As a note since you are using a protocol defined with N bytes of data. Be careful using the isr state when dealing with N >= 127 as the isr state was only designed for the slave to read 127 bytes and send 128 bytes (or less). If more is used, then the state will roll over the boundaries stated in the help file/manual and you will get states of 0x00 and 0x80 that aren't address but just the 128th or 129th bytes to read or be written by the slave, respectively. In cases of data this large, you need to monitor the D/A bit in the I2C data register in addition to the state so that if the A bit is set and the state is 0x00 or 0x80 it is indeed an address, but if the D is set, then it is just rollover data. And in the case of rollover, you have to account for that in your determination of if that byte is read or a request to write as well.
jmann



Joined: 27 Dec 2004
Posts: 21

View user's profile Send private message

PostPosted: Sat Mar 23, 2013 7:46 am     Reply with quote

Simplifying this previous example, I believe this is how we do start and stop interrupts.

SSP1CON1<3:0> would ordinarily be set to the option "0110 = I2C Slave mode, 7-bit address "
we want to set it to the option "1110 = I2C Slave mode, 7-bit address with Start and Stop bit interrupts enabled"


Code:
#use i2c(slave,sda=PIN_C4,scl=PIN_C3, address=0x30, FORCE_HW)

// create bits for the status items for  I2C
#byte SSPSTAT = 0x214 // register address specific to processor
#bit SMP = SSPSTAT.7
#bit CKE = SSPSTAT.6
#bit D_A = SSPSTAT.5
#bit P = SSPSTAT.4
#bit S = SSPSTAT.3
#bit R_W = SSPSTAT.2
#bit UA = SSPSTAT.1
#bit BF = SSPSTAT.0

#byte SSPCON1 = 0x215 // register address specific to processor

#INT_SSP
void ssp_isr(){
   SSPIF = 0;

   if(S){ // Checking for the Start Condition or Restart Condition
   // Do nothing because we got a start
   return;
   }
   if(P){ // Checking for the STOP Condition 
   ProcessI2cData(); // Do something because we got a stop
   return;
   }
        //Do the usual slave stuff here
 }

void main(){
   SSPCON1 = (SSPCON1 & 0xF0) | 0x0E ; //Modifythe interrupt type set by #USE_I2C to be 7 bit addr with start and stop interrupts
   enable_interrupts(INT_SSP);
   enable_interrupts(global);
   while(1)
   {
   }
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19326

View user's profile Send private message

PostPosted: Sat Mar 23, 2013 8:08 am     Reply with quote

Seriously, depends what chip you are using.

Several of these operations do not work correctly in many of the chips....

However the operation is basically simple:
Code:

#bit SSPM3=getenv("BIT:SSPM3")
#bit P=getenv("BIT:P")
#bit S=getenv("BIT:S")

SSPM3=TRUE; //enable interrupts on start/stop
SSPM3=FALSE; //or to disable these

//Then at the start of the interrupt handler
if (S==TRUE)
    //code here for start

if (P==TRUE)
   //code here for stop

//obviously these must exit and not call the normal I2C code


Have fun.

It is almost an 'unused' feature, and as I say on many chips won't work properly. Remember you will get start/stop interrupts for every packet on the bus.

Normally on I2C, handshaking is done by writing a trigger bit to a specific register, or reading a handshaking bit (or using a separate line). Transactions are not considered important, since you can access registers in any order and at any time.

Best Wishes
jmann



Joined: 27 Dec 2004
Posts: 21

View user's profile Send private message

PostPosted: Mon Apr 01, 2013 9:45 pm     Reply with quote

This forum became worthless over the past ten years. I'm removing my useful code example.

Last edited by jmann on Tue Apr 02, 2013 3:01 pm; edited 1 time in total
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Tue Apr 02, 2013 10:46 am     Reply with quote

The moderator believes your new question to be still on this topic and duly locked the new thread.

I did not understand your 'new' question at all.

Can you kindly re-phrase and post again.

Mike
jmann



Joined: 27 Dec 2004
Posts: 21

View user's profile Send private message

PostPosted: Wed Apr 03, 2013 9:01 am     Reply with quote

Mike Walne wrote:
The moderator believes your new question to be still on this topic and duly locked the new thread.

I did not understand your 'new' question at all.

Can you kindly re-phrase and post again.

Mike

It was a competely differen topic. This topic was about using the built-in I2C functions properly with isr_state. that topic was about interrupt behavior.

These forums become worthless. They use to be fabulous in 2005. I'm done with them and am probobly switching to the compiler Microchip discributes now.
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