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 simple communication problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
yahya.ashr@gmail.com



Joined: 10 Jun 2017
Posts: 6

View user's profile Send private message

I2C simple communication problem
PostPosted: Sat Jun 10, 2017 6:40 am     Reply with quote

Hi,

I am trying to conduct simple i2c communication between 2 PIC16f877A.

The communication seems to be not working:

Master code:
Code:

#include <16f877a.h>       
#use delay(clock=20000000)         // 20 MHz crystal on PCB
#use rs232(baud=9600,xmit=pin_c6,rcv=pin_c7)      // hardware UART; uses  RC6/TX and RC7/RX 
#use i2c(Master, sda=PIN_a4, scl=PIN_a3)
int16 i;
char j='a',result;
 
void main() {
 
   while (TRUE) {
   
     
      j=getc();
      putc(j);
      delay_us(1);

      i2c_start ();
      i2c_write (0xA0);
      i2c_write (j);
      i2c_stop();
     
      putc(j);
     
      i2c_start ();
      i2c_write (0xA1);
      result = i2c_read(0);
      i2c_stop ();
     
      output_d(result);
     
      putc(result);
     
     
     
      }
   }


Slave code:
Code:

#include <16F877a.h>
#use delay(clock=20000000)
#define SDA_PIN1 PIN_c4
#define SCL_PIN1 PIN_c3

#use i2c(SLAVE, SDA=SDA_PIN1, SCL=SCL_PIN1, address=0xA0,stream=bus,FORCE_HW )
int  state;
int k;
unsigned int8 address, buffer[16];

#INT_SSP
void ssp_interupt ()
{
 
  unsigned int8 incoming, state;

   state = i2c_isr_state();

   if(state <= 0x80)                      //Master is sending data
   {
      if(state == 0x80)
         incoming = i2c_read(2);          //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
      else
         incoming = i2c_read();

      if(state == 1)                      //First received byte is address
         address = incoming;
      else if(state >= 2 && state != 0x80)   //Received byte is data
         buffer[address++] = incoming;
   }

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


Also when i try to change the slave pins to other than the above, I get a compiling error. Would be so appreciated to point out what is the problem and how to change I2C slave pins.

Thanks in advance.
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 7:23 am     Reply with quote

The slave has to use the hardware pins.

A master can use any pins, but not the slave.

You are short-circuiting quite a few of the stages normally involved in I2C.
Your slave expects a register address, but you do not send such an address.

The sequence at a master should be:

1) i2C_start();
2) i2c_write(slave_write_address);
3) i2c_write(register_address);
//Then to read
4) i2c_start(); //this is a 'restart' command
5) i2c_write(slave_read_address);
6) i2c_read(); //this reads from 'register_address'
7) repeat reads as required, and on the last read use i2c_read(0);
8) i2c_stop();

//For write
4) i2c_write(val); //this goes to 'register_address'
5) repeat as required
6) i2c_stop();

Key things are that you always start by writing both a device address and a register address. Then to read you 'reverse' the bus, by doing a restart without stopping. You are sending 'j', where you should be sending the register address....
yahya.ashr@gmail.com



Joined: 10 Jun 2017
Posts: 6

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 7:52 am     Reply with quote

Ok, thanks but how to specify a register from the master to be written in the slave. Can you give me an example ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 8:03 am     Reply with quote

The register address you specify is where the subsequent read or write takes place.

Currently you have 16 addresses (since buffer is 16 bytes).
Beware the code ought to test the address and not allow it to go beyond the end of buffer.
You need to understand the 'two addresses'. There is the device address (byte following the start), to say 'talk to this device', and specify the direction of the transfer. This is followed (on a write), by the 'register address'.

Look at the ex_slave.c.
Then look at 2401.c
Then read the data sheet for the 24LC01 eeprom

The slave here is setup to emulate this type of eeprom, offering just 16 addresses.
yahya.ashr@gmail.com



Joined: 10 Jun 2017
Posts: 6

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 9:15 am     Reply with quote

Yes, in the EEPROM there are three bits for addressing, allowing to address up to 8 devices on the i2c bus. But in my case, i have a PIC.

My question in other words is, how to know the register address in my PIC ? Is it like [device address + something ] and how to know that something ? Smile Thanks in advance. Very Happy
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 9:33 am     Reply with quote

No, No, No.....

The I2C ADDRESS for the slave, is specified by you in your slave code. There are 112 addresses that can be used. This is the 'address=0xA0' in the slave setup. The jumpers on the EEPROM chip just specify which physical hardware address it is going to use. It's just as if you had multiple #use i2c lines and chose between them based on three switches attached to the PIC.

The address is actually a 7bit number in the top of the byte, followed by a single bit R/W flag (high for a read low for a write). So with your 'address=0xA0', your device has a write address of 0xA0, and a read address of 0xA1.

Then the EEPROM has a number of bytes of memory.
So if you look at the EEPROM code, it sends the start, then the physical device address (with the R/W bit off to say this is a 'write' transaction), and then sends the address of the byte it wants to talk to.
Then to write, it just starts sending the data.
Finally a stop.

To read, after sending the address of the byte it wants to talk to, it sends a second start (a 'restart'), followed by the read address of the chip. So it has written the 'address of the byte', and now it says 'I want to read'. It then starts reading, and changes the ACK on the last byte, then finally stops.

There are two addresses involved. The physical 'chip address', and the logical 'register address', sent one after the other.
yahya.ashr@gmail.com



Joined: 10 Jun 2017
Posts: 6

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 10:02 am     Reply with quote

Yes, but I am writing to another PIC if you re-look to my code:

What will be the range of addresses in my case ? What are the first and last addresses in my buffer ? How to know it if i am connecting more than one PIC ? Very Happy Cool

Here is the code after amendment.

Master code:
Code:

#include <16f877a.h>       
#use delay(clock=20000000)         // 20 MHz crystal on PCB
#use rs232(baud=9600,xmit=pin_c6,rcv=pin_c7)      // hardware UART; uses  RC6/TX and RC7/RX 
#use i2c(Master, sda=PIN_a4, scl=PIN_a3)
int16 i;
char j='a',result;
 
void main() {
 
   while (TRUE) {
         
      j=getc();
      putc(j);
      delay_us(1);
     
      i2c_start (); 
      i2c_write (0xA0);
      i2c_write (0x0);  //What should be put here and how to determine it?
      i2c_write (j);
      i2c_stop();
     
      putc(j);
     
      i2c_start ();
      i2c_write (0xA1);
      i2c_write (0x0); 
      result = i2c_read();
      i2c_stop ();
     
      output_d(result);
     
      putc(result);
      }
   }
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 10:40 am     Reply with quote

Yes, I know.
But you still have to do things the same way.

Code:

void main(void)
{
   while (TRUE)
   {
     
       j=getc();
       putc(j);
       delay_us(1); //why this?....
     
     
       i2c_start ();
       i2c_write (0xA0);
       i2c_write (0x0); 
//What should be put here and how to determine it?

//This is the register address you want to write to on the slave
//You need to decide how you want to lay the memory out on
//the slave chip. Depends what you want the slave to do?. So
//(for instance), if you are going to set some values and read
//some values, then design a layout with (perhaps) the write
//values at some locations followed by the ones to read.
       //you are now in write mode at address 0

       i2c_write (j); //j is now in register  on the slave
       //you could keep on writing multiple bytes here
       //they will be stored in successive bytes in the slave.
       i2c_stop(); //and finish the transaction
     
       putc(j); //this is also pointless - you have already printed j
 
       i2c_start ();
       i2c_write (0xA0);
       i2c_write (0x0);
       //You now need to set what register you want to read
       //setting it to zero again, you should get back what you have
       //written 
       i2c_start (); //This is the 'restart'. A start without a stop in front
       i2c_write (0xA1); //now we are in read mode at register 0

       result = i2c_read(0); //you must not acknowledge the last byte
       //you could read multiple bytes here. You just have to not
       //acknowledge the last byte read.

       i2c_stop ();
     
       output_d(result);
     
       putc(result);
       }
   }
}
yahya.ashr@gmail.com



Joined: 10 Jun 2017
Posts: 6

View user's profile Send private message

PostPosted: Sun Jun 11, 2017 5:40 am     Reply with quote

I changed the slave code and made it simpler. but SSP interrupt seems to be not firing (responding)

Code:

#include <16F877a.h>
#use delay(clock=20000000)

#fuses HS,NOWDT,NOPROTECT,NOLVP

#use rs232(baud=9600,xmit=pin_c6,rcv=pin_c7)
#use i2c(SLAVE, SDA=PIN_c4, SCL=PIN_c3, address=0xA0 ,force_hw )
//int  state;
int8 incoming = 0, state;
BYTE address, buffer[16];

#INT_SSP
void ssp_interupt ()
{
 state = i2c_isr_state();
 
  if(state < 0x80)   //Master is sending data
  {
  incoming=i2c_read();
 
    if(state == 1)  //First received byte is address
    { address = incoming;
    }
    if(state == 2)  //Second received byte is data
    {
    buffer[address]=incoming;
    }
    else if(state >= 0x80)
         i2c_write(buffer[address]);
   
  }
 
 
}

void main()
{

  enable_interrupts(INT_SSP);
  enable_interrupts(GLOBAL); 
 
  while(TRUE)
  {

  }
}



Am I short-circuiting any of the steps?
luckyluke



Joined: 18 Apr 2006
Posts: 45

View user's profile Send private message

PostPosted: Sun Jun 11, 2017 8:19 am     Reply with quote

have a look at this link

http://www.ccsinfo.com/forum/viewtopic.php?t=44686
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sun Jun 11, 2017 8:25 am     Reply with quote

Other thing is, one of the basic steps with I2C on the PIC, is to test your slave with the I2C bus scanner program (written by PCM_programmer), and in the code library. It's a really useful 'first thing to try'.
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sun Jun 11, 2017 8:47 am     Reply with quote

yahya.ashr@gmail.com wrote:
I changed the slave code and made it simpler. but SSP interrupt seems to be not firing (responding)

Code omitted

Am I short-circuiting any of the steps?


Yes......

Your modified code is logically wrong.
I've laid it out so the indents/comments show what is happening...
Code:

#INT_SSP
void ssp_interupt ()
{
  state = i2c_isr_state();
  if (state < 0x80)   //Master is sending data
  {
    incoming=i2c_read();
 
    if(state == 1)  //First received byte is address
    {
        address = incoming;
    }
    if(state == 2)  //Second received byte is data
    {
        buffer[address]=incoming;
    }
    //whoa.... we are still inside the 'if (state <0x80)'
    else if(state >= 0x80) //so this can never be true........
        i2c_write(buffer[address]);
  }//end bracket for the 'if (state < 0x80) test'
}
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