|
|
View previous topic :: View next topic |
Author |
Message |
pr83
Joined: 20 Jul 2006 Posts: 15
|
I2C Slave addressing Issue |
Posted: Sun Oct 15, 2006 2:39 pm |
|
|
I am using a PIC18F2520 as a SLAVE in an I2C protocol.
• When the master sends the start command, it succeeds with a command that includes the address of the SLAVE.
• There are two types of data packets the Master is interested in reading from the same SLAVE.
• Is it possible to change the 7th bit logic in the address to tell the slave what data packet it needs.
• I was thinking this may not be possible, since this would cause a change in address of the same slave, which intern would not cause the Interrupt bit to set. Unless there is a way to save two different addresses for the same Slave. This way if either of the address arrives from the Master, the Slave hardware will match it, set the ACK as well as the Interrupt bit!!! Is that true!!
Thanks |
|
|
Ttelmah Guest
|
|
Posted: Sun Oct 15, 2006 3:01 pm |
|
|
Not basically possible as you describe, but it can be done, by using GCA. On I2C, the reserved address '00', can also be programmed to trigger the interrupt. There is provision in this, to have the next packet carry configuration data for the slave. So you send '00', '04', 'new address', and the slave is setup to take the third byte, and change it's slave address. This is fully 'legal' I2C. However if the bus is only for your own use, then just send a flag to tell the slave to change modes, using GCA.
However there seems little point in doing it this way. Why not just send the 'flag' using a write, with a higher data address?.
So, assuming the device is at (say) address 24, then:
start
send 24 (data write command)
send '0' to say that we want the first type of packet
send a repeated start
send 25 (data read command)
now read the data
For the second packet type, send:
start
send 24
send '1' to say we want the second packet type
send a repeated start
send 25 (data read command)
now read the data
If you want to be able to address registers in the slave, then instead of sending 0/1, send 'address+0', and 'address+128'. (or an even higher value using two bytes if you support more than 128 useable addresses
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 15, 2006 3:09 pm |
|
|
Quote: |
There are two types of data packets the Master is interested in reading
from the same SLAVE. |
The selection of data to read from the slave is normally done with
the byte (or bytes) that follow after the Device Address byte.
Look at the CCS example, Ex_Slave.c. This example emulates
a 24C00 eeprom, with 16 bytes of data. It's in this folder:
c:\Program Files\Picc\Examples
When the Master wants to read from the PIC that's running the Ex_Slave
code, it can do it by executing this code (in the Master PIC):
BYTE read_ext_eeprom(BYTE address)
{
BYTE data;
while(!ext_eeprom_ready());
i2c_start();
i2c_write(0xa0);
i2c_write(address);
i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
return(data);
}
Look at the section of code below, in the Ex_Slave.c #int_ssp isr.
This code in the Slave will be executed when the master does
the "i2c_read(0)" line, shown above. (The address was previously sent
by the "i2c_write(address)" in the Master code).
In the code below, they're just using the address as an index into an
array. But you could do a more complex test (perhaps by checking
individual bit values in the address) and send different data back to the
Master.
Code: |
if(state == 0x80)
// Master is requesting data
{
i2c_write(buffer[address]);
} |
|
|
|
pr83
Joined: 20 Jul 2006 Posts: 15
|
|
Posted: Sun Oct 15, 2006 4:06 pm |
|
|
Thanks for the Help.
I have posted another message with my code. I wanted to know if it makes sense!! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 15, 2006 4:14 pm |
|
|
You don't have to start a new thread for your code. You can do
a post on this existing thread. You can delete your new post and
post it here. |
|
|
pr83
Joined: 20 Jul 2006 Posts: 15
|
|
Posted: Sun Oct 15, 2006 4:20 pm |
|
|
Thanks for your help. I have 3 conditons in total. I have written code for all it. I wanted to know if I am on the right track.
DECLERATIONS:
Slave Address: 0xa0
CONDITION 1:
The Master is supposed to get 13 bytes(Short Data) of data from the Slave.(SHOWN IN CODE)
CONDITION 2:
The Master is supposed to get 32 bytes(Long Data) of data from the SAME Slave.
? Change in MASTER:
Read Short data to Read Long Data:
i2c_write(10000111); (to) i2c_write(10000101);
? CHANGE IN SLAVE:
In the condition for Master is requesting for data. I will check for
if(command == 10000101)
instead of
if(command == 10000111)
? And I will change both the for loops to iterate 32 times.
CONDITION 3:
The Master is supposed to get 4 bytes of data from the SAME Slave.
The same procedure above!!
Is this method efficient?
Are there some mistakes in the code? Cause the compiler does not seem to like it. Neither does it like my way of writing address.
MASTER (SHORT DATA)
Code: |
void main()
{
int8 count; // Counter
BYTE ShortBuffer[13]; // Array for incoming Data
i2c_start();
i2c_write(10011100);// Address of the Slave
i2c_write(00000000);
i2c_start();
i2c_write(10000111); // Read Short Data Command
for(count=0;count<13;count++) // Reading 13 bytes from the slave
{
shortBuffer[count] = i2c_read(0);
}
i2c_stop();
}
|
SLAVE (SHORT DATA)
Code: |
BYTE address, command;
#INT_SSP
void ssp_interupt ()
{
BYTE incoming, state;
int i;
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
incoming = i2c_read();
if(state == 1) //First received byte is address of slave
address = incoming;
if(state == 2) //Second received byte is the coommand the master wants the slave to execute
command = incoming;
}
if(command == 10000111) //Master is requesting short data
{
for(i=0;i<13;i++) // Sending 13 bytes to the master
{
i2c_write(data[i]);
}
}
}
void main ()
{
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (TRUE) {}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 15, 2006 5:22 pm |
|
|
Your interpretation of the i2c_isr_state() values in the Ex_slave.c
example is not correct. Here's what the CCS manual says,
with some additional explanations added:
Code: |
Explanation of i2c_isr_state() returned values:
0 - Address match received with R/W bit clear.
(For example, slave address of 0xA0)
This means the Master will be writing data
to the slave in the next byte (or bytes).
1-0x7F - Master has written data; Call i2c_read()
immediately in the #int_ssp isr in the Slave
code, to get the data that was sent by the Master.
0x80 - Address match received with R/W bit set;
respond with i2c_write().
(For example, slave address of 0xA1)
This means the Master is requesting data from
the slave. Call i2c_write() immediately in the
#int_ssp isr in the Slave code, to send data
back to the Master.
0x81-0xFF - Transmission completed and acknowledged;
respond with i2c_write(). This means the Master
has received the byte that the Slave sent to it,
in response to a read operation by the Master.
The Master has sent an "ACK" bit to the slave
to show that it successfully got the byte.
This return code (0x81 to 0xFF) would only occur
if the Master is doing a read operation.
|
Here's the main part of the Ex_Slave.c code for #int_ssp.
Notice how the code doesn't save the incoming byte if
the 'state' is 0x00 or 0x80. In other words, the isr code
ignores the Slave Address byte (Either 0xA0 or 0xA1).
It saves the byte which arrives next, after the Slave address.
Code: |
state = i2c_isr_state();
if(state < 0x80) // Master is sending data
{
incoming = i2c_read();
if(state == 1) // First received byte is address
address = incoming; // for a read or write operation.
if(state == 2) // Second received byte is data
buffer[address] = incoming; // for a write operation.
}
if(state == 0x80) // Master is requesting data
{ // for a read operation.
i2c_write(buffer[address]);
}
|
In your code, you have put in comments such as:
Quote: | //First received byte is address of slave |
But that's not true. The Ex_Slave example (and your code, too)
fetches the Slave address byte, but it just throws it away.
It doesn't need it.
When the slave executes the following code, it's getting your
command byte, not the Slave address.
Code: | if(state == 1)
address = incoming;
|
You need to study the Ex_slave code and the chart in the manual
that explains the returned value for i2c_isr_state(). Study it until
you completely understand what the Ex_slave code is doing, for
both a read and a write operation (to the simulated eeprom).
Once you understand every detail, then you can successfully write
your own slave code. |
|
|
Guest
|
|
Posted: Sun Oct 15, 2006 8:34 pm |
|
|
Dear PCM Programmer,
Thanks for you help. I read the manual. Below is my new understanding for the I2C.
Slave address is 0xA1 or 0xA0.
If the Master writes 0xA1 : Slave needs to call i2c_write so the Master can read.
If the Master writes 0xA0 : Slave needs to call i2c_read so it can read what the Master is writing.
The 0 and 1 are basically clearing and setting the R/W bit.
Code: |
MASTER
void main()
{
int8 count; // Counter
BYTE ShortBuffer[13]; // Array for incoming Data
BYTE LongBuffer[32]; // Array for incoming Data
// SHORT DATA
i2c_start();
i2c_write(0xA0); // Slave address,R/W cleared
i2c_write(0x00);
i2c_write(10000111); // Read Short Data Command
i2c_start();
i2c_write(0xA1); // Slave address,R/W set
for(count=0;count<13;count++) // Reading 13 bytes from the slave
{
shortBuffer[count] = i2c_read(0);
}
i2c_stop();
// LONG DATA
count = 0;
i2c_start();
i2c_write(0xA0); // Slave address,R/W cleared
i2c_write(0x00);
i2c_write(10000001); // Read Long Data Command
i2c_start();
i2c_write(0xA1); // Slave address,R/W set
for(count=0;count<32;count++) // Reading 32 bytes from the slave
{
LongBuffer[count] = i2c_read(0);
}
i2c_stop();
}
SLAVE
BYTE address, command;
#INT_SSP
void ssp_interupt ()
{
BYTE incoming, state;
int i;
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
incoming = i2c_read();
if(state == 1) // First received byte which will be 00000000
address = incoming;
if(state == 2) //Second received byte is the coommand the master wants the slave to execute (10000111 or 10000001).
command = incoming;
}
if(state == 0x80) //Master is requesting data
{
// SHORT DATA
if(command == 10000111) //Master is requesting short data
{
i2c_write(data[0]);// write the first byte to the Master
for(i=1;(i<13);i++) // Sending 13 bytes to the master
{
if (0x81<=state<=0xFF) // Check if Master is receiving each byte.
i2c_write(data[i]);
}
}
// LONG DATA
if(command == 10000001) //Master is requesting Long data
{
i2c_write(dataL[0]);// write the first byte to the Master
for(i=1;(i<32);i++) // Sending 32 bytes to the master
{
if (0x81<=state<=0xFF) // Check if Master is receiving each byte.
i2c_write(dataL[i]);
}
}
}
}
void main ()
{
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (TRUE) {}
}
|
Questions:
1. (MASTER)Why do we always call i2c_write(0x00);
after calling i2c_write(0xA0);
2. (MASTER)Why do we always call i2c_start() second time.
3. In the MASTER and SLAVE I also wrote the code, if I needed the slave to send the 32 byte data packet.
4. I hope the i2c_isr_state() gets reset after the first i2c_stop() in the Master.
Do these seem fine!! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 15, 2006 8:51 pm |
|
|
Quote: | if (0x81<=state<=0xFF) |
I can't tell, because you didn't disable HTML when you posted the code.
So your post is full of bad statements like the one above.
Also, you logged in as "guest" so you can't edit your post and fix it.
---------------
I don't want you to post code and ask me to approve it.
You should test it, debug it, learn, and make it work.
When you do that, you can give yourself the approval. |
|
|
pr83
Joined: 20 Jul 2006 Posts: 15
|
|
Posted: Sun Oct 15, 2006 9:14 pm |
|
|
OOpppsss!!!
I apologize for that. I have disabled the HTML. I was just wanting to know if my approach is correct.
And if you think I have understood the I2C this time!!
Questions:
1. (MASTER)Why do we always call i2c_write(0x00);
after calling i2c_write(0xA0);
2. (MASTER)Why do we always call i2c_start() second time.
3. In the MASTER and SLAVE I also wrote the code, if I needed the slave to send the 32 byte data packet.
4. I hope the i2c_isr_state() gets reset after the first i2c_stop() in the Master.
Do these seem fine!!
Code: |
MASTER
void main()
{
int8 count; // Counter
BYTE ShortBuffer[13]; // Array for incoming Data
BYTE LongBuffer[32]; // Array for incoming Data
// SHORT DATA
i2c_start();
i2c_write(0xA0); // Slave address,R/W cleared
i2c_write(0x00);
i2c_write(10000111); // Read Short Data Command
i2c_start();
i2c_write(0xA1); // Slave address,R/W set
for(count=0;count<13;count++) // Reading 13 bytes from the slave
{
shortBuffer[count] = i2c_read(0);
}
i2c_stop();
// LONG DATA
count = 0;
i2c_start();
i2c_write(0xA0); // Slave address,R/W cleared
i2c_write(0x00);
i2c_write(10000001); // Read Long Data Command
i2c_start();
i2c_write(0xA1); // Slave address,R/W set
for(count=0;count<32;count++) // Reading 32 bytes from the slave
{
LongBuffer[count] = i2c_read(0);
}
i2c_stop();
}
SLAVE
BYTE address, command;
#INT_SSP
void ssp_interupt ()
{
BYTE incoming, state;
int i;
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
incoming = i2c_read();
if(state == 1) // First received byte which will be 00000000
address = incoming;
if(state == 2) //Second received byte is the coommand the master wants the slave to execute (10000111 or 10000001).
command = incoming;
}
if(state == 0x80) //Master is requesting data
{
// SHORT DATA
if(command == 10000111) //Master is requesting short data
{
i2c_write(data[0]);// write the first byte to the Master
for(i=1;(i<13);i++) // Sending 13 bytes to the master
{
if (0x81<state|| state<0xFF) //Check if Master is receiving each byte.
i2c_write(data[i]);
}
}
// LONG DATA
if(command == 10000001) //Master is requesting Long data
{
i2c_write(dataL[0]);// write the first byte to the Master
for(i=1;(i<32);i++) // Sending 32 bytes to the master
{
if (0x81<state|| state<0xFF) // Check if Master is receiving each byte.
i2c_write(dataL[i]);
}
}
}
}
void main ()
{
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (TRUE) {}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 15, 2006 11:44 pm |
|
|
Quote: |
1. (MASTER)Why do we always call i2c_write(0x00); after calling
i2c_write(0xA0); |
The Ex_Slave program emulates a 24C00 eeprom. The protocol
is defined in the eeprom data sheet.
http://ww1.microchip.com/downloads/en/DeviceDoc/21178d.pdf
Look at the sequence of bytes shown in the data sheet.
It will show you the answer.
Quote: |
2. (MASTER)Why do we always call i2c_start() second time. |
Again, look in the 24C00 data sheet.
Quote: |
3. In the MASTER and SLAVE I also wrote the code, if I needed the slave
to send the 32 byte data packet. |
Write the code to do that. Then debug and test it.
Quote: |
4. I hope the i2c_isr_state() gets reset after the first i2c_stop() in the
Master.
|
To see when the compiler clears the i2c state variable, look at
the listing file. It will have a file extension of .LST, and it's in
your project directory. Then put in comments to explain what
each line is doing. In the listing below, you can see that the
state variable is cleared whenever the i2c module receives an
address byte. Look in the PIC's data sheet to see the meaning
of each bit in the SSPSTAT register.
Code: |
...... state = i2c_isr_state();
0044: BSF STATUS.5 // Bank 1
0045: BTFSC SSPSTAT.5 // Goto 0047 if Address
0046: GOTO 04C // Goto 004C if Data
0047: BCF STATUS.5 // Bank 0
0048: CLRF @I2C_STATE // If got Address, clear state var.
0049: BTFSC SSPBUF.0 // Goto 004B if Write
004A: BSF @I2C_STATE.7 // If Read, set bit 7 of state var.
Write_operation:
004B: BSF STATUS.5 // Bank 1
Got_Data_Byte:
004C: BCF STATUS.5 // Bank 0
004D: MOVF @I2C_STATE,W // Save state var. in W
004E: INCF @I2C_STATE,F // Increment state variable
004F: MOVWF state // Return state variable
*
0099: MOVLW 03 // Upon program start-up,
009A: MOVWF @I2C_STATE // init state var. = 0x03 |
|
|
|
pr83
Joined: 20 Jul 2006 Posts: 15
|
|
Posted: Mon Oct 16, 2006 2:40 pm |
|
|
Dear PCM Programmer,
Thanks a ton for the EEPROM data sheet. It was very helpfull. |
|
|
Rhesus Guest
|
About an insctruction you're using... |
Posted: Fri Oct 27, 2006 7:08 pm |
|
|
Hey, I tried to use the i2c_isr_state() directly, though it didn't work. Where is it? Do I need to include a library or maybe it is a special function register in the PIC?. Please let me know where can I find that statement and use it, since i'm having a really tough time with the I2C bus. Currently I'm using PIC18F452 (it has hardware I2C) and CCS PCWH v. 3.225.
Thanx. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 27, 2006 7:46 pm |
|
|
Quote: | I tried to use the i2c_isr_state() directly, though it didn't work.
Currently I'm using PIC18F452 (it has hardware I2C) and CCS PCWH v. 3.225.
|
See this thread, which tells you the version number at which that
function was first available. It also provides a link to code which
emulates the i2c_isr_state() function, in case your version doesn't
support it.
http://www.ccsinfo.com/forum/viewtopic.php?t=26978 |
|
|
|
|
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
|