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 Master and Slave problem
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

i2c Master and Slave problem
PostPosted: Sat Nov 22, 2014 11:29 am     Reply with quote

Hello,

I'm looking for ideas for things to try, or that I'm missing. I have a solution where I need a PIC to be both a Master on one set of pins, and a slave on another set( both sets are hw i2c modules on the PIC). Each I2C solution is working independently, but when I combine, the slave does not operate correctly.

I've boiled down an example, where if I comment out the use for the I2C master, the slave works fine.

This is using compiler 5.019(corrected), and is using a PIC 18F97J60 on both sides.

Here is the slave code, if I comment out the #use i2c(Master) line, it works fine. This is adapted from code posted in the code library.

Code has been updated to be current based on the thread
Code:



#include <18f97j60.h>

#define I2C_DELAY_US           16

#define ID_0 0x40
#define EXPANDER_GPIO_A  0x12

#fuses HS
#fuses NOWDT                    //No Watch Dog Timer
#fuses NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#use delay(crystal=25MHz)
#use i2c(MASTER, I2C1, fast,stream=I2CM) //force_hw doesn't work when both master and slave
#use i2c(SLAVE, I2C2, address=0xC0, force_hw, stream=I2CS)

//#use i2c(SLAVE, fast, sda=PIN_D5, scl=PIN_D6, force_hw, address=0xC0)
// #use i2c(Slave, Slow, sda=PIN_C4, scl=PIN_C3, force_hw, address=0x20)


int16 data_to_send = 18100; //In the next step of the code development this data is going to change every loop (sensor data)
int16 data_sent;
int16 data_received;

#int_SSP2
void i2c_interrupt() {

    //Get state
    int state;
    int writeBuffer[2];
    int readBuffer[2];
    i2c_init(I2CS);
    state = i2c_isr_state(I2CS);

    if (state == 0) //Address match received with R/W bit clear, perform i2c_read( ) to read the I2C address.
        i2c_read(I2CS);

    else if (state == 0x80) //Address match received with R/W bit set; perform i2c_read( ) to read the I2C address, and use i2c_write( ) to pre-load the transmit buffer for the next transaction (next I2C read performed by master will read this byte).
        i2c_read(I2CS, 2);

    if (state >= 0x80) //Master is waiting for data
    {
        writeBuffer[0] = (data_to_send & 0xFF); //LSB first
        writeBuffer[1] = ((data_to_send & 0xFF00) >> 8); //MSB secound

        i2c_write(I2CS, writeBuffer[state - 0x80]); //Write appropriate byte, based on how many have already been written
        if ((state - 0x80) == 2) {
            data_sent = make16(writeBuffer[1], writeBuffer[0]);

        }
    }

    else if (state > 0) //Master has sent data; read.
    {
        readBuffer[state - 1] = i2c_read(I2CS); //LSB first and MSB secound
        if (state == 2) {
            data_received = make16(readBuffer[1], readBuffer[0]);

        }
    }
}

void main() {
    int8 r_gpio_a=0;
    enable_interrupts(INT_SSP2); //Enable interrupts
    enable_interrupts(GLOBAL);

    //  i2c_slaveAddr(0xC0);

    while (TRUE) {
        delay_ms(10);
        i2c_init(I2CM);
           delay_us(I2C_DELAY_US);
        i2c_start(I2CM);
           delay_us(I2C_DELAY_US);
        i2c_write(I2CM, ID_0); // address MCP23017 and set to write operation
           delay_us(I2C_DELAY_US);
        i2c_write(I2CM, EXPANDER_GPIO_A); // write number of register to read
           delay_us(I2C_DELAY_US);
        i2c_start(I2CM); // restart
           delay_us(I2C_DELAY_US);
        i2c_write(I2CM, ID_0 + 1); // address MCP23017 and set to read operation
           delay_us(I2C_DELAY_US);
        r_gpio_a = i2c_read(I2CM, 0); // read register
           delay_us(I2C_DELAY_US);
        i2c_stop(I2CM);

    }

}


Here is the Master

Code:

/*
 * File:   I2CMaster.c
 * Author: julrigg
 *
 * Created on November 3, 2014, 6:17 PM
 */
#include <18f97j60.h>

#fuses HS

#fuses NOWDT                    //No Watch Dog Timer
#fuses NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#use delay(crystal=25MHz)
#use i2c(master, fast, I2C1,force_hw)

/* Bit defines */
#define EEPROM_SCL PIN_C3
#define EEPROM_SDA PIN_C4
#define SLAVE_ADDRESS 0xC0// addr=2 reserved, note address masking.
#define I2C_DELAY_US           20 // worked 20 //Worked 32 //75 //100
#define I2C_INTERBYTE_DELAY_US 60 // worked 80
#byte PIC_SSPCON2  = 0xfc5

void initI2C (void);
void writeI2C (int16 word, unsigned int slave_addr);
int16 readI2C (unsigned int slave_addr);
int16 mainI2C ();

void initI2C (void)
{
   output_float(EEPROM_SCL);
   output_float(EEPROM_SDA);
}

void writeI2C (int16 word, unsigned int slave_addr)
{
   i2c_start();
   delay_us(I2C_DELAY_US);
   i2c_write(SLAVE_ADDRESS);  /* Device Address */
   delay_us(I2C_DELAY_US);
   i2c_write(word & 0x00ff);            //LSB first
   delay_us(I2C_DELAY_US);
   i2c_write((word & 0xff00) >> 8);     //MSB second
   delay_us(I2C_DELAY_US);
   i2c_stop();
   delay_us(I2C_DELAY_US);
}

int16 readI2C (unsigned int slave_addr)
{
   int8 b1=0, b2=0;
   i2c_start();   // restart condition
   delay_us(I2C_DELAY_US);
   i2c_write(SLAVE_ADDRESS + 1);
   delay_us(I2C_INTERBYTE_DELAY_US);
   b1 = i2c_read(1);
   delay_us(I2C_INTERBYTE_DELAY_US);
   b2 = i2c_read(0);
   delay_us(I2C_DELAY_US);
   i2c_stop();
   return make16(b2, b1);
}

void main(){
int16 data_to_send=18099;
int16 data_received;

initI2C();
while(true){
delay_ms(20);

  // Write
  writeI2C(data_to_send, SLAVE_ADDRESS);

//  printf("Message sent successfully from the Master to the Slave: %ld\n",data_to_send);

  // Read
  data_received = readI2C(SLAVE_ADDRESS);

  if (data_received == data_to_send +1)
      output_high(PIN_C1);
  else
      output_low(PIN_C1);
//  printf("Answer received successfully from the Slave to the Master: %ld\n",data_received);
//  printf("\n");
}
}


Using a Logic Analyzer, it looks like in the failure case, the slave is not releasing the scl line after the first write by the master



Any thoughts or advice appreciated.

Regards

riggsy


Last edited by riggsy on Sun Nov 23, 2014 10:52 pm; edited 6 times in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19346

View user's profile Send private message

PostPosted: Sat Nov 22, 2014 1:04 pm     Reply with quote

#USE I2C, supports use of the option I2C1, and I2C2, to allow two hardware ports to be used. You need this or you haven't a hope of getting both hardware ports to work...
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Sat Nov 22, 2014 4:00 pm     Reply with quote

Thanks Ttelmah for the suggestion.

I tried it out, and am still getting the same response from the hardware.

I've updated my first post to show the changes I made.

I'm reading through the online manual (my printed is for V4). I see it make mention of this:

Quote:
The I2C library contains functions to implement an I2C bus. The #USE I2C remains in effect for the I2C_START, I2C_STOP, I2C_READ, I2C_WRITE and I2C_POLL functions until another USE I2C is encountered.


In your opinion, should I be initializing i2c prior to the master or slave for each call prior to making the call( using NOINIT in the use statement, and i2c_init(stream) prior to each i2c call), or should I be able to init both, and use them via their stream name?


Regards

riggsy


Last edited by riggsy on Sun Nov 23, 2014 1:17 pm; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Nov 22, 2014 4:56 pm     Reply with quote

What's your compiler version ? This is not the version:
Quote:
This is using compiler 5.0.19

Look at the top of your .LST file in your project directory after a
successful compilation. The version is a 4-digit number such as 4.141
or 5.032, etc.
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Sat Nov 22, 2014 5:59 pm     Reply with quote

5.019 from the .lst
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Nov 22, 2014 7:19 pm     Reply with quote

I installed vs. 5.019 and looked at the .LST file. I also tried various
changes and compared the differences in the .LST files with ExamDiff.
http://www.prestosoft.com/edp_examdiff.asp (vs. 1.9)

I found that moving the master line below the slave line makes it behave
the same as if the master line was commented out. It generates more
code for the i2c_read() operation in the interrupt routine.
Code:

#use i2c(SLAVE, I2C2, fast, force_hw, address=0xC0, stream=I2CS)
#use i2c(MASTER, I2C1, fast, stream=I2CM)


One thing I do have to ask is, your first program has #use statements
for both master and slave, but there is no code in it to use the master.
If you're testing these things simultaneously, shouldn't your test program
have some master code in it ? In main() ? Also, what slave device is the
master code talking to ?
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Sun Nov 23, 2014 1:24 pm     Reply with quote

Thanks for you insights PCM,

You are correct that the test program should use the I2C Master to really know it's working.

The master side is coded and working already, and talks to several MCP23017's.

The slave side communication is a separate module that was just delivered.

My test code above was the initial effort to get communication working with the new module. Once it was working, I made the addition of the #use i2c for the master i2c, which broke the slave code (which was not as concise a process I have just described ).

Ttelmah's response yesterday led me to change to using the hardware modules by name in the #use i2c calls. If I add an init_i2c(I2CS) call in the interrupt routine of the slave, it allows the slave code to run correctly, but as you point out, the master line isn't really being used.

I'll add in a call to one of the 23017's, so that it is utilizing both i2c lines, and see how it goes.

I'll update the code in the first post with the current state and post the progress, at the end of today( with it working hopefully Cool ).

Thank you for your help, your responses have already opened up some things for me to try.

Regards

riggsy
Ttelmah



Joined: 11 Mar 2010
Posts: 19346

View user's profile Send private message

PostPosted: Sun Nov 23, 2014 2:04 pm     Reply with quote

Try explicitly acking the read.
On 'non stream' use the compiler defaults to ack, I wonder if on streams it doesn't. So:
i2c_read(I2CS,1);

Just a thought.

As a comment, get rid of the 'FAST' on the slave. Slave devices don't have a speed.
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Sun Nov 23, 2014 10:51 pm     Reply with quote

So I've updated the code listing in the first post with the present state of the test code.

I've added some master code into the i2c slave to read from a MCP23017 occasionally.

Presently this code works, but will hang up in the debugger after >3-8 seconds(The i2c line exhibits the problem as in the graphic above). It seems more stable if I run the communication faster (1 ms delay in the master while loop).

I'm not sure if the hang is due to debugger interference (a 15 yard penalty, automatic 1st down, but I am running two computers with a debugger on each side), or is a timing related problem where at some point things line up to create the problem.

Does CCS better document how to keep the i2c lines straight somewhere?

Ttelmah, I did try explicitly acking the read, it did not have an effect on the problem.

Regards

riggsy
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Nov 23, 2014 11:58 pm     Reply with quote

What if you don't run the PICs in debug mode. What if you compile
and run the PICs in release mode ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19346

View user's profile Send private message

PostPosted: Mon Nov 24, 2014 2:42 am     Reply with quote

Let's step back for a moment.

In the original question, the comment is made, that "it looks like in the failure case, the slave is not releasing the scl line after the first write by the master".
Think again.
A released I2C line, is _high_. The line is released.

In I2C, the master clock the SCL low, and then the slave can hold it low after the last clock, till the transaction is completed. It is the SDA line that is odd, not being released. The slave device has no control on SDA...
What I can't tell from the diagram (needs the analyser set to a faster timebase), is how many clock pulses there are in the transaction to this point?.
I'd like to see a much larger graph of what is happening here. If it wasn't for the 'it works without the master code' behaviour, I'd be fractionally wondering if (for instance) the pull-up resistors were a little high in value, and an edge was incorrectly being seen here. Remember that the I2C inputs will be using ST logic levels (unless you specify, and your chip supports the SMBUS levels), so may well not see a line as high, that your logic analyser does see as high (also depending on the programming of the analyser). It almost looks on the published picture as if SDA might be rising while SCL is high, which will be seen as a stop condition.
Can you post a larger diagram at this point?.
What are your pull up values?.
Length of bus?.
Have you tried lowering the bus rate?. Does it work at 100KHz?.
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Mon Nov 24, 2014 12:51 pm     Reply with quote

Thanks both of you for your thoughts.

Ttelmah, you bring up good points.

The hardware on this design(not under my control) does have a longer i2c path than I have programmed for in the past, and also crosses a ribbon cable. So all told, ~20cm (8").

Pullups are on the Slave end of the run, and are 2.2k at present. Would moving to something smaller (1K perhaps) help, given the line length?

I pulled the debuggers out of the equation, and it still hangs up.

If I change to slow, the above code runs reliably! ( apparently the Ricky Bobby in me wants to go fast!).

The master side (as in i2c_master.c) also has i2c memory, digital pots, and MCP23017's to communicate with( as well as running ethernet), so my preference would be to have it running at the faster speed, if possible.

Here is a closer look at what happens when the i2c line goes dead at the faster speed.




Regards

riggsy
temtronic



Joined: 01 Jul 2010
Posts: 9164
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Nov 24, 2014 2:02 pm     Reply with quote

1) I'd reduce the pullups to 1K, maybe 1.2K. Easy to just parallel the existing ones with 2K2 and see what happens...

2) re: ribbon cable. Normally every other conductor should be ground to reduce noise, cross talk and well have a GREAT ground.

That being said, since it world at 100K my hunch is #1 not #2.

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19346

View user's profile Send private message

PostPosted: Mon Nov 24, 2014 2:35 pm     Reply with quote

Remember you can go faster that slow, but slower than fast.
You can actually specify the clock rate, so:

FAST=200000

for example.

That it works at slow, does suggest that the problem may be the lines not going up properly at the speed needed. If possible can you measure the capacitance?. I2C pullups needed are dependent on the speed, chip voltage, and the bus capacitance. It does sound rather a high capacitance setup, and 1.8K, is quite a large resistor for a 3.3v device. You can legally go slightly under 1KR, even with the minimum 3mA drivers.

Try reducing the resistors, and the SMBUS option. This uses a lower 'high' voltage. I suspect the slave is not always seeing the clock.
riggsy



Joined: 09 Sep 2008
Posts: 9

View user's profile Send private message

PostPosted: Mon Nov 24, 2014 3:56 pm     Reply with quote

Lowering the pullup to 1.1K has the above code working reliably at 400khz. Without the pullup change, it would work up to ~200khz.

Thank you for your help and ideas. I think this is working to the point where I can integrate it with the rest of the solution.

I've been reading this forum for years, and most of the help I've needed has come from using the search option on this forum, from posts you guys have made helping other users.

In addition to working code, I feel I've got a little bit better understanding of the i2c module now(not complete, but better). Thanks for that!

Regards

riggsy
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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