|
|
View previous topic :: View next topic |
Author |
Message |
jds-pic
Joined: 17 Sep 2003 Posts: 205
|
i2c slave code example which emulates a 24cXX EEPORM? |
Posted: Wed Nov 02, 2005 3:07 pm |
|
|
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
|
|
Posted: Wed Nov 02, 2005 3:31 pm |
|
|
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
|
|
Posted: Wed Nov 02, 2005 3:31 pm |
|
|
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
|
|
Posted: Wed Nov 02, 2005 3:42 pm |
|
|
with great shame, i slink away. i should have looked at the examples first.
thanks,
jds-pic |
|
|
Ttelmah Guest
|
|
Posted: Thu Nov 03, 2005 4:32 am |
|
|
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
|
|
Posted: Thu Nov 03, 2005 10:29 am |
|
|
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
|
|
Posted: Thu Nov 03, 2005 11:31 am |
|
|
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
|
|
Posted: Thu Nov 03, 2005 1:45 pm |
|
|
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.
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
|
|
Posted: Mon Nov 07, 2005 10:07 am |
|
|
Ttelmah,
did you have a moment to look at my questions above?
thanks
jds-pic |
|
|
Ttelmah Guest
|
|
Posted: Mon Nov 07, 2005 11:04 am |
|
|
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 |
|
|
|
|
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
|