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 code example which emulates a 24cXX EEPORM?

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



Joined: 17 Sep 2003
Posts: 205

View user's profile Send private message

i2c slave code example which emulates a 24cXX EEPORM?
PostPosted: Wed Nov 02, 2005 3:07 pm     Reply with quote

hi,

does anyone have some example i2c SLAVE code which would allow a PIC to mimic a generic 24cXX EEPROM? in other words, the PIC, for all intents and purposes, would end up looking exactly like a 24c series EEPROM, allowing another i2c-connected microcontroller to read (~64) bytewide "memory locations" from the PIC in the same way it would an Atmel or Microchip EEPROM.

ps: the PIC in interest has hardware i2c, PIC 18F6520.

thanks for any ideas etc.

regards,
jds-pic
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Nov 02, 2005 3:31 pm     Reply with quote

Look at the CCS example file, EX_SLAVE.C.
This file is in c:\Program Files\Picc\Examples
Mark



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

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

PostPosted: Wed Nov 02, 2005 3:31 pm     Reply with quote

Yep, CCS gives you one:
Code:
///////////////////////////////////////////////////////////////////////////
////                         EX_SLAVE.C                                ////
////                                                                   ////
////  This program uses the PIC in I2C slave mode to emulate the       ////
////  24LC01 EEPROM. You can write to addresses 00h to 0Fh with it.    ////
////                                                                   ////
////  This program is to be used in conjunction with the ex_extee.c    ////
////  sample.  Use the "#include <2402.C>" or "#include <2401.c>".     ////
////  Only 16 bytes of address space are implemented, however.         ////
////                                                                   ////
////  If using a compiler version before 2.639 add "*0x14 = 0x3E;" to  ////
////  the begining of main(), and add "NOFORCE_SW" as the last         ////
////  parameter in the #use i2c directive.                             ////
////                                                                   ////
////  This example will work with the PCM and PCH compilers.  The    ////
////  following conditional compilation lines are used to include a  ////
////  valid device for each compiler.  Change the device, clock and  ////
////  RS232 pins for your hardware if needed.                        ////
/////////////////////////////////////////////////////////////////////////
////        (C) Copyright 1996,2003 Custom Computer Services         ////
//// This source code may only be used by licensed users of the CCS  ////
//// C compiler.  This source code may only be distributed to other  ////
//// licensed users of the CCS C compiler.  No other use,            ////
//// reproduction or distribution is permitted without written       ////
//// permission.  Derivative programs created using this software    ////
//// in object code form are not restricted in any way.              ////
/////////////////////////////////////////////////////////////////////////
jds-pic



Joined: 17 Sep 2003
Posts: 205

View user's profile Send private message

PostPosted: Wed Nov 02, 2005 3:42 pm     Reply with quote

with great shame, i slink away. i should have looked at the examples first.

Embarassed

thanks,
jds-pic
Ttelmah
Guest







PostPosted: Thu Nov 03, 2005 4:32 am     Reply with quote

Some 'caveats' though with the code as given.
Obviously, you would need to 'extend' this to automatically increment the memory address, and step through a larger 'block'. However the real problem is a hidden 'annoyance', in the I2C_WRITE function, when used this way in an interrupt. The function as implemented, _waits_ for the byte to be sent. Now obviously, if the chip is doing anything else in the main code, you do not want this to happen, relying instead on the interrupt to signal transfers. Also the write, clears the interrupt, which the handler also clears. At high speeds, this can cause 'hiccups' in the transfer...
I wrote this a while ago, to transfer a 16byte block,into a 18F252, and modified it recently, to use the 'new' I2C_STATE' function:

Code:

#BYTE SSPBUFF=0xFC9
#BYTE SSPCON1=0xFC6
#bit SBIT=SSPCON1.4

int1 transfer_in_progress=FALSE;
int8 I2C_BUFF[16];
int8 byte_address=0;

#INT_SSP
void ssp_interupt (void) {
   BYTE received, state;
   state = i2c_isr_state();
   if(state < 0x80) {            
      //Here master is sending data
      received = i2c_read(1);
      if (state==1) {
         byte_address=received;
         transfer_in_progress=true;
      }
      else {
         I2C_BUFF[byte_address++]=incoming;
         if (byte_address>=16) {
             //I flag transfer as finished after 16 bytes
             byte_address=0;
             //This ensures I do not overrun buffer
             transfer_in_progress=false;
         }
      }
   }
   else {
      //Here I am to send data
      SSPBUFF=I2C_BUFF[byte_address++];
      SBIT=1;
      //This replaces the I2C_WRITE, using the hardware.
      if (byte_address==16) {
          //I flag transfer as finished after 16 bytes
          byte_address=0;
          //This ensures I do not overrun buffer
          transfer_in_progress=false;
          }
    }
}


Best Wishes
jds-pic



Joined: 17 Sep 2003
Posts: 205

View user's profile Send private message

PostPosted: Thu Nov 03, 2005 10:29 am     Reply with quote

Ttelmah wrote:

Code:

#BYTE SSPBUFF=0xFC9
#BYTE SSPCON1=0xFC6
#bit SBIT=SSPCON1.4

int1 transfer_in_progress=FALSE;
int8 I2C_BUFF[16];
int8 byte_address=0;

#INT_SSP
void ssp_interupt (void) {
   BYTE received, state;
   state = i2c_isr_state();
   if(state < 0x80) {            
      //Here master is sending data
      received = i2c_read(1);
      if (state==1) {
         byte_address=received;
         transfer_in_progress=true;
      }
      else {
         I2C_BUFF[byte_address++]=incoming;
         if (byte_address>=16) {
             //I flag transfer as finished after 16 bytes
             byte_address=0;
             //This ensures I do not overrun buffer
             transfer_in_progress=false;
         }
      }
   }
   else {
      //Here I am to send data
      SSPBUFF=I2C_BUFF[byte_address++];
      SBIT=1;
      //This replaces the I2C_WRITE, using the hardware.
      if (byte_address==16) {
          //I flag transfer as finished after 16 bytes
          byte_address=0;
          //This ensures I do not overrun buffer
          transfer_in_progress=false;
          }
    }
}



Ttelmah,
thanks for the hints on the HW-assist.
would you elaborate a little on the following?
1) how you use transfer_in_progress in your mainline code
2) how you manage the byte_address in your mainline code
3) where did incoming in "I2C_BUFF[byte_address++]=incoming;" come from

thanks,
jim / jds-pic
Ttelmah
Guest







PostPosted: Thu Nov 03, 2005 11:31 am     Reply with quote

incoming=received. The block was copied from two different parts, and I hadn't spotted that I used a different variable name in them...
You don't do anything with the address in the main. The whole point is that you have a block of memory, and transfer to/from this block. You can change the contents of the block in the main, which can then be read by the master, or you can write data to the block from the master, and then read this in the main.
You use the flsg just as with any other flag of this sort. If (for instance), you 'main' is doing some task involving I/O etc., then you have something like:
Code:

while (true){
    //Main permanent loop
    if (transfer in progress) {
        //Here execute the code you want to run while a transfer is
        //taking place.
    }
    else {
       //Here execute the other 'normal' code.
    }

This is the standard way of 'process switching', by an event signalled from an interrupt. The 'latency', will depend on how long the main 'stays' inside the actual 'normal' code.

Best Wishes
jds-pic



Joined: 17 Sep 2003
Posts: 205

View user's profile Send private message

PostPosted: Thu Nov 03, 2005 1:45 pm     Reply with quote

Ttelmah,

you've been a great help; thanks for the clear explanation above. sorry if it sounds like a novice on this end. in truth, i have been programming pics for a long time, but this is actually the first time with i2c as a slave on the pic -- and i am concerned from a realtime perspective because the master can poll me at any time. so please bear with me. Cool

note: the attached i2c master has to be able to read from random "EEPROM" locations, one byte at a time. so there is one i2c transaction per returned byte (i.e., no "page reads"). similarly, the attached i2c master has to be able to write to random "EEPROM" locations, one byte at a time. again, one i2c transaction per write.

my hardware-assisted 24cxx emulation function is shaping up as thus:
Code:

#define VIRTUAL_EEPROM_SIZE 128  // address range 0-127
int buffer[VIRTUAL_EEPROM_SIZE];
int address=0;

#INT_SSP
void ssp_interrupt () {
   int 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) && (address < (VIRTUAL_EEPROM_SIZE))) // second received byte is data
         buffer[address] = incoming;
   }
   if(state == 0x80)   {                  // master is requesting data
      SSPBUFF=(buffer[address]);
      SBIT=1;  // hardware based i2c write.
   }
}


one thing i am a bit confused about however. during operation, the ISR is called when the first byte is rec'd via i2c. state=1 and this byte is read into address. the ISR exits. according to the i2c_isr_state() docs, the state increments per rec'd byte.

so, the master sends the next byte, which causes another jump into the ISR at the slave. now state=2? --> because of the normal 1 + the byte counter incremented? ok, so this second byte is the data to write. we copy that into the buffer.

now the confusion starts in. it would seem to me that i now have to explicitly clear the byte counter or the next transaction is going to be hosed up (!). reason being, state=3, 4, 5, and so on with subsequent rec'd bytes. and finally, state will wrap to 0 again (127->0), causing an error condition.

how to solve?

also, how is client -> master transmission handled? in other words, what kicks the ISR to result in state=0x80? how did we get into the ISR?

finally, from purely an academic standpoint, how is i2c_isr_state() in any way advantageous over i2c_poll()?

thanks again for all of your help.
jim / jds-pic

ps
CCS's i2c_isr_state() man page = WORST EVER.
jds-pic



Joined: 17 Sep 2003
Posts: 205

View user's profile Send private message

PostPosted: Mon Nov 07, 2005 10:07 am     Reply with quote

Ttelmah,
did you have a moment to look at my questions above?

thanks
jds-pic
Ttelmah
Guest







PostPosted: Mon Nov 07, 2005 11:04 am     Reply with quote

Sorry, I was doing other things, and missed the posting.
The top bit of the 'state', is set from the data direction bit in the first 'address' transaction (not the internal data 'address', but the save devices address). So when you ask for a 'read' from the slave device, this bit gets set.
Yes, you will have to do some more fiddling if a block can be longer than 128bytes. However normally you should be dealing with blocks of 64 bytes or less (my own stuff used a 16byte block), so this would not then be a problem.
For block transfers, you need to have the address increment on each transaction, and have the second 'state' test in the 'write' code as:

if (state>=2)

which then allows successive bytes to be written.

Similarly on the 'read', you would use:

if (state & 0x80)

which would accept state 80, 81 etc...
Generally, block transfers are 'thick' in real devices, and will wrap if you try to transfer past the end of a block. So error checking for this should exist in the master.

Best Wishes
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