PIC18F4550 I2C

Introduction to I2C protocol 

  • I2C (Inter-Integrated Circuit) is a master-slave protocol that may have one master or many master and many slaves whereas SPI has only one master.
  • It is a synchronous serial protocol that communicates data between two devices.
  • It is generally used for communication over a short distance. It is a slow speed protocol as compared to SPI communication.
  • The I2C device has 7-bit or 10-bit unique address. So to access these devices, the master has to address them by the 7-bit or 10-bit unique address.
  • I2C is used in many applications like reading RTC (Real-time clock), accessing external EEPROM memory. It is also used in sensor modules like gyro, magnetometer, etc.
  • It is also called as Two Wire Interface (TWI) protocol.

I2C bus uses 2 lines for communication:

  1. Serial Clock (SCL): It is a clock signal. Data will be sent to other devices on the clock tick event. The only master device has control over this SCL line.
  2. Serial Data(SDA): It is a serial data line which is used to send data in either direction.
Generalized I2C Lines
Generalized I2C Lines

The I2C bus is an open-drain configuration which means they can pull the corresponding signal line low but cannot drive it high. Hence the line will go into an unknown state. In order to avoid this, pull up resistors need to be connected to SCL and SDA pins.


PIC18F4550 I2C Pins

PIC18F4550 I2C Pins

PORTB.0 and PORTB.1 pins are used for I2C communication as SDA and SCL respectively.

PIC18F4550 has Master Synchronous Serial Protocol module (MSSP) which includes I2C protocol.

I2C Registers

To establish I2C communication between devices, we have to configure PIC18F4550. To do this, PIC18F4550 has different registers which are as follows

SSPBUF: Serial Transmit/Receive Buffer

In I2C protocol, SSPBUF is a data register which is used for transmission and reception of data.

SSPSR: MSSP Shift Register

It is a shift register that is used to shift data in and out on the SDA line. It is not directly accessible.

SSPADD Register: MSSP Address Register

In Master mode:

SSPADD register is used to decide or generate a baud/bit rate.

Now how to calculate the value for baud/bit rate.

I2C Baud Generation
I2C Baud Generation


Load the generated value in the SSPADD register.

The above formula is used to decide I2C clock frequency.

In Slave mode:

SSPADD register holds the address of the slave device.


SSPCON1: MSSP Control 1 Register

PIC18F4550 SSPCON1 Register
PIC18F4550 SSPCON1 Register

Bit 7 – WCOL: Write Collision Detect bit

       1 = SSPBUF Register is written while it is still transmitting the previous word.

       0 = No collision.

Bit 6 – SSPOV: Receive overflow indicator flag

       1 = A byte is received while SSPBUF Register is still holding the previous word.

       0 = No overflow.

Bit 5 – SSPEN: Synchronous Serial Port Enable bit

       1 = Enables the Serial Port and configures SDA and SCL pin as a serial port pin.

       0 = Disable Serial Port and configure these pins as GPIO.

Bit 4 – CKP: SCK Release control bit

In slave mode:

        1 = Release clock

        0 = Holds clock low

Not used in master mode

Bit 3:0 – SSPM3:SSPM0:

        0110 = I2C Slave mode, 7-bit address

        0111 = I2C Slave mode, 10-bit address

        1000 = I2C Master mode, Clock=FOSC / (4*(SSPADD+1))

        1011 = I2C Firmware Controlled Master mode

        1110 = I2C Slave mode, 7-bit address with Start and Stop interrupt enabled

        1111 = I2C Slave mode, 10-bit address with Start and Stop interrupt enabled


SSPCON2: MSSP Control 2 Register

PIC18F4550 SSPCON2 Register
PIC18F4550 SSPCON2 Register

Bit 6 – ACKSTAT: Acknowledgement Status bit

            1 = acknowledgment signal was not received from the slave

            0 = acknowledgment signal was received from the slave

Bit 5 – ACKDT: Acknowledge Data bit

This bit is used when the master requests to slave devices for reading/receiving of data.

            1 = Not acknowledge which tells other devices stop sending data.

            0 = Acknowledge which tells the device to send another data.

Bit 4 – ACKEN: Acknowledge Sequence Enable bit

            1 = Initiate acknowledge sequence on SDA and SCL pins and transmit ACKDT data bit which is automatically cleared by hardware.

            0 = Acknowledge sequence is disabled or not in use.

Bit 3 – RCEN: Receive Enable bit

            1 = Enable receive mode for I2C.

            0 = Disable receive mode.

Bit 2 – PEN: Stop Condition Enable bit

            1 = Enable stop condition and cleared automatically by hardware.

            0 = Stop condition is Idle.

Bit 1 – RSEN: Restart Condition Enable bit

            1 = Enable repeated start condition and cleared automatically by hardware.

            0 = Repeated start condition is Idle.

Bit 0 – SEN: Start Condition Enable bit

            1 = Enable start condition and cleared automatically by hardware.

            0 = Start condition is Idle.


SSPSTAT: MSSP Status Register

PIC18F4550 SSPSTAT Register
PIC18F4550 SSPSTAT Register

Bit 7 – SMP: Slew Rate Control bit

In Master or Slave mode:

            1 = Slew rate control disabled for standard speed mode (100 kHz and 1MHz). 

            0 = Slew rate control enabled for high speed mode (400 kHz).

Bit 6 – CKE: SMBus Select bit

            1 = Enable SMBus specific input.

            0 = Disable SMBus specific input.

Bit 5 – D/A: Data/Address bit

In Slave mode:

            1 = indicates that the last byte received or transmitted was data

            0 = indicates that the last byte received or transmitted was address     

Bit 4 – P: Stop bit

            1 = indicates that the stop bit has been detected last

            0 = stop bit was not detected last

Bit 3 – S: Start bit

            1 = indicates that the start bit has been detected last

            0 = start bit was not detected last

Bit 2 – R/W: Read/Write Information bit

In Slave mode:

            1 = Read

            0 = Write

In Master mode:

            1 = Transmit is in progress

            0 = Transmit is not in progress

Bit 1 – UA: Update Address bit.

It is used for 10-bit slave mode only.

            1 = Indicates that the user needs to update the address in the SSPADD register.

            0 = Address does not need to be updated

Bit 0 – BF: Buffer Full Status bit

In Transmit mode:

            1 = SSPBUF is full.

            0 = SSPBUF is empty.

In Receive mode:

            1 = SSPBUF is full (does not include ACK and stop bit.)

            0 = SSPBUF is empty.


I2C Clock Frequency

  • Only the master initiates the clock on the SCL line.
  • SSPADD register in the master is used to generate I2C clock frequency.

Status Flag and Interrupt Flag

  • After complete transmission of data/address, Buffer full flag is set, and also acknowledge signal is received. After reception of the acknowledge signal, the SSPIF interrupt flag is set.
  • Whereas in data reception, the Buffer full flag is set when a complete byte is received and the device needs to send an Acknowledge signal for indication of reading continuation. And when data is shifted from SSPSR register to SSPBUF, SSPIF flag is set.  
  • So to check whether the transmission/reception is complete or not, we have to monitor either BF flag or the SSPIF interrupt flag.


Programming for PIC18F4550 I2C Communication


  • Configure PORTB.0 and PORTB.1 as an input pin.
  • Also, configure the SSPSTAT and SSPCON2 register.
  • Enable I2C by setting SSPEN bit in the SSPCON1 register. Also select master/slave mode.
  • Set frequency of I2C communication by loading value in the SSPADD register in master mode.


void I2C_Init()
    TRISB0=1;		/* Set up I2C lines by setting as input */
    SSPSTAT=0x80;		/* Slew rate disabled, other bits are cleared */
    SSPCON1=0x28;	/* Enable SSP port for I2C Master mode,
			clock = FOSC / (4 * (SSPADD+1))*/ 
    SSPADD=Bit_rate;	/* Clock 100 kHz */  
    SSPIE=1;		/* Enable SSPIF interrupt */


Start I2C communication

  • Send start pulse to the slave device by setting a bit in SEN in the SSPCON2 register.
  • Wait for SEN bit to clear.
  • Also, check S bit in SSPSTAT register which indicates the start bit is detected or not.
  • And then send the device slave address along with write operation and check for acknowledgment signal.
char I2C_Start(char slave_write_address)
    SSPCON2bits.SEN=1;		/* Send start pulse */
    while(SSPCON2bits.SEN);	/* Wait for completion of start pulse */
    if(!SSPSTATbits.S)		/* Check whether START detected last */
    return 0;			/* Return 0 to indicate start failed */   
    return (I2C_Write(slave_write_address));	/* Write slave device address
						with write to communicate */


Write/Transmit Data

We have to monitor BF flag or SSPIF which will set when transmission/reception of a byte is complete.

I2C Bus Ready

Check the BF flag and R/W bit in the SSPSTAT register for finding the I2C bus is ready or not.

 void I2C_Ready()
    while(BCLIF);	/* Wait if bit collision interrupt flag is set*/

    /* Wait for Buffer full and read write flag*/
    while(SSPSTATbits.BF || (SSPSTATbits.R_nW));
    SSPIF=0;  		/* Clear SSPIF interrupt flag*/


Also SSPIF flag can be used to poll the complete byte transmit/receive status. Also, it is necessary to clear the SSPIF interrupt flag through software.

void I2C_Ready()
    while(!SSPIF);	/* Wait for operation complete */
    SSPIF=0;		/* Clear SSPIF interrupt flag*/


Copy/write data to the SSPBUF register for transmission and wait for the completion of the transmission.

char I2C_Write(unsigned char data)
      SSPBUF=data;	/* Write data to SSPBUF*/
      if (ACKSTAT)	/* Check for acknowledge bit*/
        return 1;
        return 2;


After write if we want to Stop I2C communication,

Set the PEN bit in SSPCON2 register and wait for the PEN bit to clear.

Also, check P bit in the SSPSTAT register which indicates the stop bit is detected.

char I2C_Stop()
    PEN=1;		/* Stop communication*/
    while(PEN);		/* Wait for end of stop pulse*/
    SSPIF = 0;
    if (!SSPSTATbits.P) /* Check whether STOP is detected last */
       return 0;	/* If not return 0 to indicate start failed*/


Read/Receive Data

  • Enable receive mode by setting bit RCEN in SSPCON2.
  • Then wait for the Buffer flag to set which is set when a complete byte is received.
  • Read the buffer immediately.
  • After reading one byte, send an acknowledge signal if we want to read the next data else send negative acknowledge which tells we don’t want to read the next data.
  • To do this, ACKDT =1 or 0 and ACKEN should be enabled.
/* Read data from location and send ack or nack depending upon parameter*/

char I2C_Read(char flag)
        int buffer=0;
        RCEN=1;			/* Enable receive */

	/* Wait for buffer full flag which when complete byte received */
        buffer=SSPBUF;		/* Copy SSPBUF to buffer */

	/* Send acknowledgment or negative acknowledgment after read to 
	continue or stop reading */



void I2C_Ack()
    ACKDT=0;		/* Acknowledge data 1:NACK,0:ACK */
    ACKEN=1;		/* Enable ACK to send */


Negative Acknowledgement

void I2C_Nack()
    ACKDT=1;		/* Acknowledge data 1:NACK,0:ACK */
    ACKEN=1;		/* Enable ACK to send */

Components Used