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 master&slave problem

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



Joined: 23 May 2007
Posts: 22

View user's profile Send private message

i2c master&slave problem
PostPosted: Mon Apr 28, 2008 1:49 am     Reply with quote

master PIC4523 is unable to receive the value which slave PIC16F877 transmits via the I2C, the PC terminator can't display any letter
the code is following

master
Code:

#include <18F4523.h>                             // PICF4523 header file
#device ADC=12                                   // 12 bits ADC
#use delay(clock=4000000)                       // for 4Mhz crystal
#fuses XT, NOWDT, NOPROTECT, NOLVP               // for debug mode
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, Parity=N, Bits=8, ERRORS)

#use i2c(Master, sda=PIN_C4, scl=PIN_C3)

void main()
{
      int8 data[8] = {0x01,0x02,0x03,0x04,0x05,0x06};
      
      
      i2c_start();
      i2c_write(0xA1);
      data[0] = i2c_read();
      data[1] = i2c_read();
      data[2] = i2c_read();
      data[3] = i2c_read();
      data[4] = i2c_read();
      data[5] = i2c_read(0);
      i2c_stop();
      
      delay_ms(100);
      
      while(1)
         {
            putc(data[0]);
            putc(data[1]);
            putc(data[2]);
            putc(data[3]);
            putc(data[4]);
            putc(data[5]);
                  
            delay_ms(100);   
         }     
}


slave
Code:

#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0xa0)

BYTE buffer[0x10] = {0x50,0x51,0x52,0x53,0x54,0x55} ;


#INT_SSP
void ssp_interupt ()
{
   BYTE  state;

   state = i2c_isr_state();

 
   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[0]);
      i2c_write(buffer[1]);
      i2c_write(buffer[2]);
      i2c_write(buffer[3]);
      i2c_write(buffer[4]);
      i2c_write(buffer[5]);
   }
}

void main ()
{
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_SSP);

   while (TRUE) {}
}
Matro
Guest







PostPosted: Mon Apr 28, 2008 4:17 am     Reply with quote

You can try to add "FORCE_HW" to both of your #use i2c(..) instructions.

Be also aware that your slave shall absolutely be running before you start the master.

Matro.
Ttelmah
Guest







PostPosted: Mon Apr 28, 2008 7:08 am     Reply with quote

I'd also say you ought to read the address on the first I2C call, to empty the buffer, and really only send one byte at a time. So:
Code:

#INT_SSP
void ssp_interupt ()
{
   BYTE  state;
   static int8 ctr=0;
   int8 dummy;

   state = i2c_isr_state();

 
   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[ctr++]);
   }
   else {
      dummy=i2c_read();
      ctr=0;
   }
}

As another 'comment', why 'HS', in the slave. This engages the high gain oscillator (designed fo higher frequency crystals), and increases the risk of oscillator problems, if used at lower frequencies, unless you have a series resistor to limit the gain of the whole circuit.
Obvious final comment - you have got thepull-up resistors on the I2C lines?.

Best Wishes
Guest








PostPosted: Mon Apr 28, 2008 7:16 am     Reply with quote

Ttelmah wrote:
I'd also say you ought to read the address on the first I2C call, to empty the buffer, and really only send one byte at a time. So:
Code:

#INT_SSP
void ssp_interupt ()
{
   BYTE  state;
   static int8 ctr=0;
   int8 dummy;

   state = i2c_isr_state();

 
   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[ctr++]);
   }
   else {
      dummy=i2c_read();
      ctr=0;
   }
}


Even if it is better that is not needed here. The address will give an i2c_isr_state of 0x80 (that's a read).
But once again, it can only be better. ;-)

Matro
LEVENT



Joined: 04 May 2006
Posts: 16

View user's profile Send private message

PostPosted: Mon Apr 28, 2008 7:59 am     Reply with quote

Hello,
I agree with Guest..Try to send one data by every one interrupt.
stefsun



Joined: 23 May 2007
Posts: 22

View user's profile Send private message

PostPosted: Mon Apr 28, 2008 8:24 am     Reply with quote

I read all of the replies,I know a lot.thank you Matro,Ttelmah and LEVENT
I understand the #INT_SSP and i2c_isr_state(),send one byte at a time.

master call i2c_read() function,then slave will call the interrupt #INT_SSP,every interrupt judge the state of i2c_isr_state(),then respond with i2c_write() or i2c_read().

'HS' I copy others program foget to modify it.thanks again.

I modify my program, then try it.
Ttelmah
Guest







PostPosted: Mon Apr 28, 2008 8:56 am     Reply with quote

Yes, I'm not used to using the internal I2C state function (tend to use my own). In which case, he should read in the '80', and output the first byte in this. So:
Code:

#INT_SSP
void ssp_interupt ()
{
   BYTE  state;
   static int8 ctr=0;
   int8 dummy;

   state = i2c_isr_state();

 
   if(state >0x80)                     //Master is requesting data
   {
      i2c_write(buffer[ctr++]);
   }
   else {
      if (state==0x80) {
         dummy=i2c_read();
         i2c_write(buffer[0]);
         ctr =1;
      }
      else dummy=i2c_read();
   }
}


Best Wishes
Matro
Guest







PostPosted: Mon Apr 28, 2008 9:04 am     Reply with quote

The above code is enough for the posted example :
Code:


#INT_SSP
void ssp_interupt ()
{
   BYTE  state,dummy;

   state = i2c_isr_state();
   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[state - 0x80]);
   }
   else
   {
      dummy=i2c_read();    //this line is to avoid locking the bus in case of a write
   }
}


A good improvement is to add in the main() a line to clear SSPOV bit.
Another one is to use the NOCLEAR directive with the ISR and to clear interrupt flag at the beginning of the ISR.

Matro
rnielsen



Joined: 23 Sep 2003
Posts: 852
Location: Utah

View user's profile Send private message

PostPosted: Mon Apr 28, 2008 12:25 pm     Reply with quote

Some people have the initial problem with timing between the master & slave units. Both of your PICs are running at the same speed. The master might end up sending commands faster than the slave can process them. Try placing delays between each of the master's commands and see if that helps any. You can experiment with the delays to get the optimum timing. An o-scope is very useful to work on the delay times.

Doing only one I2C action in the slave interrupt is the best way to do it.

Ronald
Guest








PostPosted: Mon Apr 28, 2008 1:06 pm     Reply with quote

Matro wrote:
The above code is enough for the posted example :
Code:


#INT_SSP
void ssp_interupt ()
{
   BYTE  state,dummy;

   state = i2c_isr_state();
   if(state >= 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[state - 0x80]);
   }
   else
   {
      dummy=i2c_read();    //this line is to avoid locking the bus in case of a write
   }
}


A good improvement is to add in the main() a line to clear SSPOV bit.
Another one is to use the NOCLEAR directive with the ISR and to clear interrupt flag at the beginning of the ISR.

Matro


Hi Matro,

I tried your code above with a master request below
Code:

i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);


After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.

I have tried to solve this by

Code:

#INT_SSP
void ssp_interupt ()
{
   BYTE  state,dummy;

   state = i2c_isr_state();
   if(state == 0x80)                     //Master is requesting data
   {
      i2c_write(buffer[state - 0x80]);
   }
   if(state > 0x80)         
   {
      //do nothing after sent the 1st data
   }
   else
   {
      dummy=i2c_read();   
   }
}


however, is there any other ways to solve it if the slave dunno how many data the master requested?
Thank you.
Matro
Guest







PostPosted: Tue Apr 29, 2008 1:31 am     Reply with quote

Anonymous wrote:

Hi Matro,

I tried your code above with a master request below
Code:

i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);


After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.

Normally, you shouldn't have this behavior because the read is NACKed.
What should happen is :
The slave receives the address+read and trigger the SSP interrupt with state = 0x80.
So you place the buffer[0] in the I²C buffer and release the clock (that's the purpose of i2c_write). The master send a clock and NACK the 1st byte (that's what i2c_read(0) does).
Since the data is NACKed, the slave shouldn't trigger an interrupt again and should stop the transfer.

Normally, the code I gave should work as it is, and the problem is otherwhere I guess. Could you post a complete "compilable" code that demonstrates the problem (with fuses and all preprocessor directives)?

Matro.
stefsun



Joined: 23 May 2007
Posts: 22

View user's profile Send private message

PostPosted: Tue Apr 29, 2008 1:53 am     Reply with quote

thanks every one ,everything is ok
steven
Guest







PostPosted: Tue Apr 29, 2008 11:07 am     Reply with quote

Matro wrote:
Anonymous wrote:

Hi Matro,

I tried your code above with a master request below
Code:

i2c_start();
i2c_write(0xa1);
data=i2c_read(0);
i2c_stop();
lcd_putc(data);


After the master received the 1st data, the slave will be hang.
I found that the problem is cause by the state.
when the 1st request the state is 0x80, the slave sent buffer[0], after that the state will change to 0x81...and the slave will try to sent buffer[1]. However the slave will hang while master din request for second data.

Normally, you shouldn't have this behavior because the read is NACKed.
What should happen is :
The slave receives the address+read and trigger the SSP interrupt with state = 0x80.
So you place the buffer[0] in the I²C buffer and release the clock (that's the purpose of i2c_write). The master send a clock and NACK the 1st byte (that's what i2c_read(0) does).
Since the data is NACKed, the slave shouldn't trigger an interrupt again and should stop the transfer.

Normally, the code I gave should work as it is, and the problem is otherwhere I guess. Could you post a complete "compilable" code that demonstrates the problem (with fuses and all preprocessor directives)?

Matro.


master.c
Code:

#include <16F877a.h>
#use delay (clock = 20000000)
#fuses HS, NOWDT,NOPROTECT,NOLVP
#include <lcd.c>
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)

void main()
{
   unsigned char data;
   lcd_init();
   while (1)
   {

   i2c_start();
   i2c_write(0xa0);
   i2c_write(0x41);
   i2c_write(0x42);
   i2c_write(0x43);

   i2c_start();
   i2c_write(0xa1);
   data=i2c_read();
   lcd_putc(data);
   data=i2c_read();
   lcd_putc(data);
   data=i2c_read(0);   
   i2c_stop();
   lcd_putc(data);

   output_high(PIN_A0);
   delay_ms(500);
   output_low(PIN_A0);
   delay_ms(500);
   }
}


slave.c
Code:

#include <16F877a.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include <lcd.c>
#use i2c(slave, address=0xa0, SDA=PIN_C4, SCL=PIN_C3 )

BYTE buffer[0x10] = {0x41,0x42,0x43,0x44,0x45,0x46} ;


#INT_SSP
void ssp_interupt ()
{
   BYTE  state,dummy;
   clear_interrupt(int_SSP);
   state = i2c_isr_state();
   if(state >= 0x80)                 
   {
      i2c_write(buffer[state - 0x80]);
   }
   else
   {
      dummy=i2c_read();   
      lcd_putc(dummy);
   }
}

void main()
{
   lcd_init();
   enable_interrupts(INT_SSP);
   enable_interrupts(global);
   
   while (1)
   {
      //i2c_write(0x50);
   }
}


thanks for helping.

steven
stefsun



Joined: 23 May 2007
Posts: 22

View user's profile Send private message

PostPosted: Tue Apr 29, 2008 7:09 pm     Reply with quote

master.c
Code:

#include <16F877a.h>
#use delay (clock = 20000000)
#fuses HS, NOWDT,NOPROTECT,NOLVP
#include <lcd.c>
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)

void main()
{
   unsigned char data;
   lcd_init();
   while (1)
   {

   i2c_start();
   i2c_write(0xa0);
   i2c_write(0x41);
   i2c_write(0x42);
   i2c_write(0x43);

   i2c_start();
   i2c_write(0xa1);
   data=i2c_read();
   lcd_putc(data);
   data=i2c_read();
   lcd_putc(data);
   data=i2c_read(0);   
   i2c_stop();
   lcd_putc(data);

   output_high(PIN_A0);
   delay_ms(500);
   output_low(PIN_A0);
   delay_ms(500);
   }
}




thanks for helping.

steven[/quote]


i2c_start();
i2c_write(0xa0);
i2c_write(0x41);
i2c_write(0x42);
i2c_write(0x43);

these statements are not needed
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