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 CCS Technical Support

i2c problem: double addresses and inconsistent data?

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



Joined: 02 Feb 2005
Posts: 9
Location: New York City

View user's profile Send private message Visit poster's website

i2c problem: double addresses and inconsistent data?
PostPosted: Wed Feb 02, 2005 8:56 pm     Reply with quote

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

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

PostPosted: Wed Feb 02, 2005 9:06 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Wed Feb 02, 2005 9:12 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Wed Feb 02, 2005 9:18 pm     Reply with quote

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

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

PostPosted: Wed Feb 02, 2005 9:39 pm     Reply with quote

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 Wink )
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

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

PostPosted: Wed Feb 02, 2005 9:42 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Thu Feb 03, 2005 10:49 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Fri Feb 04, 2005 5:48 pm     Reply with quote

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?!
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