PIC16 and I2C

I2C (Inter Integrated Circuits) is a very popular communication protocol. I have written tutorials about interfacing I2C with STM32 and LPC2148 and today we are going to interface I2C with PIC16 microcontroller. I am using PIC16f877A chip and in this tutorial we will cover the software part and the actual hardware will be covered in the upcoming tutorials.


WORKING

Working of I2C is pretty simple. In order to write data  or read data from the slave, we have to follow particular sequences as given below:-
To WRITE data, do the following
START -> WRITE SLAVE ADDRESS -> WRITE REGISTER ADDRESS -> WRITE DATA -> STOP
To read data from the slave, do
START -> WRITE SLAVE ADDRESS -> WRITE REGISTER ADDRESS -> REPEATED START ->WRITE SLAVE ADDRESS + 0x01 -> READ DATA -> STOP
So I will just get into the code now.

SOME INSIGHT INTO THE CODE

INITIALISE

To initialise I2C in PIC16, do the following:-
First clear all bits in SSPSTAT register
SSPSTAT=0;

Next We need to set some bits in SSPCON1 register

SSPCON = 0b00101000;



Bits 0 to 3 are set as ‘1000’ which indicates I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) . I have modified this formula so that we can set our desired clock in the SSPADD register nd the formula is

SSPADD = (_XTAL_FREQ/(4*clock))-1;

The 5th bit i.e. SSPEN Enables the serial port and configures the SDA and SCL pins as the serial port pins .

Next we need to set Pins for the I2C SDA and SCL so
TRISC3 = 1;
TRISC4 = 1;

Both are set as input and make sure you connect a pull-up register to these pins.

void I2C_init (uint32_t clock)
{
    SSPADD = (_XTAL_FREQ/(4*clock))-1;  // here clock is the BR/clock speed    
    SSPCON = 0b00101000;     //first 4 bits I2c master mode , 6th bit is SSPEN enables the scl and sda line
    SSPSTAT = 0;
    TRISC3 = 1;
    TRISC4 = 1;
}


I2C WAIT

I2C_wait is a very important function. Whenever we do some transaction in I2C, some of the bits got set and than they are programmed to be cleared automatically by the hardware. I2C_wait function wait until the bits are cleared so after that we can do another transaction
I2C_wait (void)
{
    while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));    // wait for start bit to clear in SSPSTAT and bits 0 to 4 in SSPCON2
}

I2C START

This is used to start the I2C by setting up the bit 0 (SEN) in the SSPCON2 register
void I2C_start (void)
{
    I2C_wait ();
    SSPCON2 |= 0x01; // SEN=1 -> initiate the START condition on SDA and SCL pins
}

REPEATED START

This function is used while reading data from the slave. As mentioned above, we need to send a repeated start condition before sending the slave address. To enable repeated start, we need to set bit 1 (RSEN) in the SSPCON2register.
void I2C_repeated_start (void)
{
    I2C_wait();
    SSPCON2 |= 0x02; // RSEN=1  -> initiate REPEATED START condition on SDA and SCL pins
}

I2C STOP

This is used to stop I2C by setting up the bit 2 (PEN) in the SSPCON2 register
void I2C_stop (void)
{
    I2C_wait ();
    SSPCON2 |= 0x04; // PEN=1 -> initiate the STOP condition on SDA and SCL pins
}

WRITE

This function is used for writing data/address to the slave. We have to write data into the SSPBUF buffer
void I2C_write (uint8_t data)
{
    I2C_wait ();
    SSPBUF = data;  // load data into SSPBUF register
}

READ

This is used to read data from slave. Remember we have to give ‘ack’ as the argument here, which will inform the slave that whether you want to read another byte of data (ack=0) or you want to stop receiving (ack=1)
uint8_t I2C_read (uint8_t ack)
{
    uint8_t temp;
    I2C_wait();
    RCEN = 1;    // enable receive mode for I2c 
    I2C_wait();
    temp = SSPBUF;   // load data from Buffer to the temp
    I2C_wait();
    ACKDT = (ack);  // 0-- not ACK , 1-- ACK
    ACKEN = 1;   // Send Acknowledgement
    return temp;
}

Ok so this sums up our functions Now let’s see how to write and read data to/ from the slave

WRITE DATA TO SLAVE


I2C_init (100000); // initialise i2c @ 100 KHz
    uint8_t data;
    
    while (1)
    {
        I2C_start ();  // start I2C
        I2C_write (0xA0); // using 24aa00 slave address
        I2C_write (0x01);  // writing into register 0x01
        I2C_write ('f');  // write data
        I2C_stop ();  // stop I2C
        
        __delay_ms (2000);   // 2sec delay
        
    }

The writing procedure is same as mentioned in the beginning of the post i.e. START -> WRITE SLAVE ADDRESS -> WRITE REGISTER ADDRESS -> WRITE DATA -> STOP.
The functioned does exactly the same and if we take a look at the proteus I2C debugger you will see that

  • Here observe that first microcontroller sends ‘S’ i.e. START
  • Next it send ‘A0’ i.e slave address (0xA0)
  • than slave sends ‘A’ i.e. slave acknowledge that it is present
  • Next the microcontroller sends 0x66 (‘f’)  which is acknowledged by the slave by send ‘A’
  • and finally microcontroller sends P (Stop)

READ DATA FROM SLAVE

I2C_init (100000); // initialise i2c @ 100 KHz
    uint8_t data;
    
    while (1)
    {
        I2C_start ();  // start I2C
        I2C_write (0xA0); // using 24aa00 slave address
        I2C_write (0x01);  // writing into register 0x01
        I2C_write (0xA0|0x01); // using 24aa00 slave address + read
        data = I2C_read (1);  // read data and send NACK
        I2C_stop ();
        
        __delay_ms (2000);   // 2sec delay
        
    }

The reading process is as follows
START -> WRITE SLAVE ADDRESS -> WRITE REGISTER ADDRESS -> REPEATED START ->WRITE SLAVE ADDRESS + 0x01 -> READ DATA -> STOP

Let’s take a look at the proteus 
Here first row is for writing data so just ignore it for this part.
In order to read data, 
  • microcontroller sends ‘S’ (Start)
  • next it sends slave address (0xA0)
  • Than it sends register address from where it wants to read data (0x01)
  • next ‘Sr’ (repeated start)
  • Now the slave sends the data stored in the register i.e. 0x66 (‘f’)
  • microcontroller sends ‘N’ (NACK –> Not ACK) to indicate that it doesn’t want to receive anymore data
  • microcontroller sends ‘P’ (Stop)
This is it for this tutorial. I will show the actual hardware interfacing in the next one.
To DOWNLOAD full CODE, visit 

PIC16 and I2C PIC16 and I2C Reviewed by arun singh on July 29, 2018 Rating: 5
Powered by Blogger.