|
|
View previous topic :: View next topic |
Author |
Message |
e
Joined: 02 Feb 2005 Posts: 9 Location: New York City
|
i2c problem: double addresses and inconsistent data? |
Posted: Wed Feb 02, 2005 8:56 pm |
|
|
I've been trying to use i2c for the first time. Been playing around for a few days, getting very inconsistent results. Have read everything I could find on this forum, but to no avail. So some newbie questions:
the following code is running on two PIC18F452s at 20Mhz using CCS 3.6.0.97, with pullup resistors on the SDA and SCL lines (i've tried 1k, 4.7k, and 10k). the bus lines are very short, under 10cm long. i have tried both SLOW and FAST modes.
very rarely I get rs232 output that looks okay, i.e. every 2 seconds i see an incremented value output - although I didn't expect to see the address chars sent out. For example (160 is the dec equiv of slave address 0xa0):
slave starting...
160
0
160
1
160
2
160
3
however after about 8 or 9 iterations, suddenly i just get pairs of address chars (160,160 - pause - 160,160). usually, i ONLY get the address chars from the beginning, in pairs (meaning one followed immediately by another), but sometimes just one at a time.
Master:
Code: |
#include <18F452.h>
#device *=16 // use full RAM
#use delay(clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use rs232(baud=38400, parity=N, xmit=PIN_C6, rcv=PIN_C7, stream=RS232, bits=8)
#use i2c(MASTER,sda=PIN_C4,scl=PIN_C3,FAST,FORCE_HW)
#define powerPin PIN_C5
#define cMax 64
void main() {
int8 c = 0;
delay_ms(1000); // initial power-up delay (long to allow slaves to start up)
output_bit(powerPin, 1);
while (TRUE) {
i2c_start();
i2c_write(0xa0); // send slave address
i2c_write(c); // send data char
i2c_stop();
if (++c >= cMax) { // increment data char
c = 0;
}
delay_ms(2000);
}
}
|
Slave:
Code: |
#include <18F452.h>
#device *=16 // use full RAM
#use delay(clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use rs232(baud=38400, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
#use i2c(SLAVE,sda=PIN_C4,scl=PIN_C3,address=0xa0,FAST,FORCE_HW)
#define powerPin PIN_C5
void main() {
int8 c;
delay_ms(500); // initial power-up delay
output_bit(powerPin, 1);
printf("slave starting...\n\r");
while (TRUE) {
if (i2c_poll()) {
c= i2c_read();
printf("%u\n\r",c);
}
}
}
|
i have also tried this without the i2c_poll, because in this simple example I don't care if the slave hangs on the i2c_read. i have also tried using interrupts on the slave, which seem to be triggered correctly; however the results are the same.
obviously my biggest question is, why are things not working? but more broadly I am curious - if i2c_poll() waits for a valid char, why would i2c_read() ever return the address char - isn't this a waste of time, since i2c_poll should only respond to its own slave address?
i know newbie i2c questions are constantly being posted but any help greatly appreciated. i have read the Philips spec, btw, but have hoped not to have to manually control lines to figure out what's up. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Feb 02, 2005 9:06 pm |
|
|
Yes you will get the address. You can determine if it is data or address by looking at the sspstat register. Check out the datasheet. |
|
|
e
Joined: 02 Feb 2005 Posts: 9 Location: New York City
|
|
Posted: Wed Feb 02, 2005 9:12 pm |
|
|
thanks for the reply - but why would i get address/data pairs for a little while, then seem to fall "out of sync"? here is some sample output:
slave starting...
160
0
160
1
160
2
160
3
160
4
160
5
160
160
160
160
160
160
the point at which the datachar stops arriving ranges from immediately to after 20-30 iterations. |
|
|
e
Joined: 02 Feb 2005 Posts: 9 Location: New York City
|
|
Posted: Wed Feb 02, 2005 9:18 pm |
|
|
also, i have noticed in looking at i2c examples sometimes people seem to expect the address char and explicitly ignore it in order to wait for the data, and other times people act like the first char read after an i2cpoll must be the data. i have noticed this inconsistency even within one PIC C textbook. (I have been teaching myself C over the last week with "Embedded C Programming and the Microchip PIC" which uses CCS.)
regardless, all I really care about is getting consistent data comm between my uP's :-) . |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Feb 02, 2005 9:39 pm |
|
|
Well, obviously some people aren't going to do it right. The ex_slave.c example is not done right in my opinion. Too many assumptions without checking status bits. Here is a receive routine that I use that is called from the SSP interrupt.
Code: | /****************************************************************************
* DESCRIPTION: Handles the Access.bus reception.
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void ABUS_Rx_Byte(void)
{
static uint8_t rx_count = 0; // num of bytes received
static int8_t rx_msg_len = 0; // msg length
static uint8_t checksum = 0; // message checksum (XOR)
static uint8_t data = 0; // data read from SSPBUF
if (SSPSTATbits.P)
{
ABUS_Status = I2C_IDLE;
// inter-message wait time in ms
ABUS_Wait_To_Send = 5;
ABUS_Hardware_Timeout = 0;
}
else
{
// were we addressed in read mode
if (SSPSTATbits.R_W)
{
if (ABUS_Rx_Data.hdr.stat == CHECKSUM_OK)
{
ABUS_Rx_Data.hdr.stat = PROCESS_OK;
// Load acknowledgement value
SSPBUF = MSGACK;
}
else
{
SSPBUF = MSGNACK;
}
// Setup to allow data to be read from us
SSPCON1bits.CKP=1;
ABUS_Status = I2C_SLAVE_TxING;
ABUS_Hardware_Timeout = 25;
}
// See if we were addressed
// The following line was modified because of errata with
// the PIC18CXX2 clearing the BF bit when the BSR is pointed to 0x0F
// and an instruction contains 0xC9 in its 8 least significant bits
// else if (SSPSTATbits.BF)
else if ((SSPSTATbits.BF) || (ABUS_Status == I2C_RxING))
{
// continue recieving bytes
// process the first byte
if (!SSPSTATbits.D_A)
{
// Read the data
data = SSPBUF;
ABUS_LED_ON();
ABUS_LED_Timeout = ABUS_LED_TIME;
ABUS_Status = I2C_RxING;
ABUS_Rx_Data.hdr.stat = INVALID_MSG;
ABUS_Rx_Data.hdr.dest = data;
rx_msg_len = 2;
rx_count = 1;
checksum = data;
}
else
{
// Read the data
data = SSPBUF;
++rx_count; // keep track of how many bytes
if (rx_count == 3) // test for length of msg byte
rx_msg_len = data & 0x7F; // set length counter, ignore MSB
else
--rx_msg_len;
if (rx_msg_len == -1) // check for end of msg
{
ABUS_Status = I2C_WAITING;
if ((checksum ^ data) == 0)
ABUS_Rx_Data.hdr.stat = CHECKSUM_OK;
else
ABUS_Rx_Data.hdr.stat = INVALID_MSG;
}
checksum ^= data;
if (rx_count < sizeof(ABUS_Rx_Data.buf))
ABUS_Rx_Data.buf[rx_count] = data;
}
ABUS_Hardware_Timeout = 25;
}
}
} |
We use a modified version of the Access.bus. We added an acknowledgement to the messages to ensure that the slave gets the message properly. I have done a lot (too much) with i2c communications between pics in multi-master mode. One of these days maybe I'll post some sample code in the library. Til then, look at this and ask away. To get you started though:
1. I don't use interrupts to transmit my data (mainly because they didn't have it when I started and it ain't broke so don't fix it )
2. Receive the message in the slave using interrupts.
3. The message will be loaded into a buffer and a flag set.
4. The main loop of the slave "looks for" this flag to be set and then processes the message.
5. The flag is cleared so that the isr routine and receive another message.
Now depending on your design, you may opt to have multiple receive buffers in which case you could receive another message while processing the first. We have an embedded 586 133MHz broadcasting messages to the pics and they keep up pretty well with just a single buffer. The ack/nack handles those messages that fail if the pic is still busy like storing configuration data into an eeprom. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Feb 02, 2005 9:42 pm |
|
|
Oh yeah, here is an isr handler:
Code: |
static void Interrupt_SSP(void)
{
/* Make sure the buffer has not overflowed */
if (SSPCON1bits.SSPOV)
{
ABUS_Reset_Hardware();
}
else
{
ABUS_Wait_To_Send = 5;
// don't recieve a byte if we are already processing a msg
if (ABUS_Rx_Data.hdr.stat != PROCESS_OK)
ABUS_Rx_Byte();
}
return;
}
|
Notice the checking of the SSPOV bit. Put a check in your code and see if you are getting an overflow. |
|
|
e
Joined: 02 Feb 2005 Posts: 9 Location: New York City
|
|
Posted: Thu Feb 03, 2005 10:49 am |
|
|
Mark, this is very useful - thanks. I will go over this in depth before I post any more questions. |
|
|
e
Joined: 02 Feb 2005 Posts: 9 Location: New York City
|
|
Posted: Fri Feb 04, 2005 5:48 pm |
|
|
okay i'm obviously doing something wrong that is very elementary. i have stripped down my hardware to nothing but Pics on a protoboard and 1 MAX233 for RS232 debugging comm. i can't get any of the CCS examples or PIC C book examples to work.
mark - i've looked through your code and i think i understand (most of) it, however before i try to delve fully into it i want to know why the simplest possible test doesn't work on my setup.
as i said before, my slave always gets its device address byte from the master but rarely gets the subsequent data byte. (By address I mean the slave address, not the EEPROM data address.)
when running the CCS example EX_EXTEE and EX_SLAVE i can see that the slave gets its SSP interrupt triggered and sees its device address but hangs in the ext_eeprom_ready() subroutine which is called from write_ext_eeprom(). so presumably the problem lies in the ACK not getting sent back properly. here is CCS code for those routines (they are actually from 2402.c):
Code: | BOOLEAN ext_eeprom_ready() {
int1 ack;
i2c_start(); // If the write command is acknowledged,
ack = i2c_write(0xa0); // then the device is ready.
i2c_stop();
return !ack;
}
void write_ext_eeprom(BYTE address, BYTE data) {
while(!ext_eeprom_ready());
i2c_start();
i2c_write(0xa0);
i2c_write(address);
i2c_write(data);
i2c_stop();
}
|
i must be doing something really stupid wrong. I am using two PIC18F452s at 20Mhz. Is it possible this is too fast? Power is decoupled. The only other thing on the board is a MAX233 with a serial cable. Ground is shared between the 2 PICs. The i2C bus is solid core wire about 10cm long. I don't know how to measure exact capacitance. I have tried 1k, 2k, 4k and 10k SDA/SCL pullup resistors.
What am I missing?! |
|
|
|
|
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
|