PIC16F877A I2C Tutorial

This article is a continuation of the series of tutorials on the PIC16F877A Microcontroller. The aim of this series is to provide easy and practical examples that anyone can understand. In our previous tutorial, we have seen PIC16F877A ADC Tutorial. Now we are going to see PIC16F877A I2C Tutorial.

Prerequisites

Before getting into the I2C Tutorial of PIC16F877A, Please read the below topics….

PIC16F877A I2C Tutorial

Introduction

I²C is a serial computer bus, which is invented by NXP semiconductors (Philips semiconductors). The I²C bus is used to attach low-speed peripheral integrated circuits to microcontrollers and processors. I²C bus uses two bidirectional open-drain lines such as SDA (serial data line) and SCL (serial clock line) and these are pulled up with resistors. I²C bus permits a master device to start communication with a slave device. Data is interchanged between these two devices. Typical voltages used are +3.3V or +5V although systems with extra voltages are allowed. Nowadays new microcontrollers have inbuilt I²C Registers. PIC16F877A also has separate registers for I2C. Unlike UART, you can connect and communicate to multiple devices using the same I2C bus.

Most of the PIC microcontrollers have built-in Master Synchronous Serial Port (MSSP).

Master Synchronous Serial Port (MSSP)

The Master Synchronous Serial Port (MSSP) module is a serial interface, useful for communicating with other peripheral or microcontroller devices. These peripheral devices may be serial EEPROMs, shift registers, display drivers, A/D converters, etc. The MSSP module can operate in one of two modes:

• Serial Peripheral Interface (SPI)
• Inter-Integrated Circuit (I2C)

The I2C interface supports the following modes in hardware:

• Master mode
• Multi-Master mode
• Slave mode

In this tutorial, we will learn how to operate the MSSP module of the PIC Microcontroller as an I2C master. And EEPROM will act as a slave.

The MSSP module in I2C mode fully implements all master and slave functions (including general call support) and provides interrupts on Start and Stop bits in hardware to determine a free bus (multi-master function). The MSSP module implements the standard mode specifications, as well as 7-bit and 10-bit addressing. Two pins are used for data transfer:

  • Serial clock (SCL) – RC3/SCK/SCL
  • Serial data (SDA) – RC4/SDI/SDA

The user must configure these pins as inputs or outputs through the TRISC<4:3> bits.

Registers Used for I2C

The MSSP module has three associated registers. These three registers are used for I2C.

The MSSP module has six registers for I2C operation. These are:

  • MSSP Status Register (SSPSTAT)
  • MSSP Control Register 1 (SSPCON1)
  • MSSP Control Register 2 (SSPCON2)
  • Serial Receive/Transmit Buffer Register (SSPBUF)
  • MSSP Shift Register (SSPSR) – Not directly accessible
  • MSSP Address Register (SSPADD)

SSPSTAT – MSSP Status Register

This register is the status register of the MSSP Module. The lower six bits of the SSPSTAT are read-only. The upper two bits of the SSPSTAT are read/write.

BF: This is the Buffer Full status bit. In the transmit mode this bit will set when we write data to SSPBUF register and it is cleared when the data is shifted out. In the receive mode, this bit will set when the data or address is received in the SSPBUF register and it is cleared when we read the SSPBUF register.

UA: This is the Update Address bit and is used only in 10-bit address mode. It indicates that the user needs to update the address in the SSPADD register.

R/W: This is the Read/Write bit information. In the slave mode, it indicates the status of the R/W bit during the last address match. In the master mode, 1 indicates that transmit is in progress and vice versa.

S: This bit indicates that a Start bit is detected last and it will be cleared automatically during Reset.

P: As above, this bit indicates that a Stop bit is detected last and it will be cleared automatically during Reset.

D/A: This is the data or addresses indicator bit and it is used only in slave mode. If it is set, the last byte received was data otherwise it will be addressed.

CKE: Setting this bit enables SMBus specific inputs. SMBus is another bus similar to I2C, which is compatible with each other.

SMP: Setting this bit disables slew rate control and vice versa.

SSPCON1 – MSSP Control Register 1

The SSPCON1 register is readable and writable, which is used to control the I2C.

SSPM0 ~ SSPM3: These are synchronous serial port mode select bits.

1111 = I2C Slave mode, 10-bit address with Start and Stop bit interrupts enabled
1110 = I2C Slave mode, 7-bit address with Start and Stop bit interrupts enabled
1011 = I2C Firmware Controlled Master mode (Slave Idle)
1000 = I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
0111 = I2C Slave mode, 10-bit address
0110 = I2C Slave mode, 7-bit address

CKP: This is the SCL clock release control bit. It is used only in slave mode. Setting this bit releases the clock. If zero, it holds the clock (clock stretch).

SSPEN:  This is the synchronous serial port enable bit. Setting this bit enables the serial port.

SSPOV: This is receiving an overflow indicator bit. If this bit is set during receive mode, it indicates that a byte is received while SSPBUF is holding the previous value. And it has no application in transmit mode. We must clear this bit in the software.

WCOL: It is the write collision detect bit. If this bit is set during master to transmit mode, it indicates that a write to the SSPBUF register was attempted when I2C conditions were not valid for a transmission to be started. And if it is set during a slave to transmit mode, it indicates that the SSPBUF register is written when it is transmitting the previous word. We must clear this bit in the software.

SSPCON2 – MSSP Control Register 2

The SSPCON2 register is readable and writable, which is used to control the I2C.

SEN: Start Condition or Stretch Enable bit. In master mode, setting this bit to initiate start condition on SCL & SDA pins and it will be automatically cleared by the hardware. And in slave mode setting this bit enables clock stretching for both slave receive and slave transmit. If it is cleared in slave mode, clock stretching is enabled only for slave transmit.

RSEN: Repeated start condition enable bit. This bit has application only in master mode. Setting this bit will initiate repeated start condition on both SCL & SDA pins and it will be cleared automatically in hardware.

PEN: Stop condition enable bit. This bit has application only in master mode. Setting this bit will initiate stop condition on both SCL & SDA pins and it will be automatically cleared in hardware.

RCEN: Receive enable bit. This bit also has application only in master mode. Setting this bit enables receive mode for I2C.

ACKEN: Acknowledge sequence enable bit. Setting this bit initiates acknowledge sequence on SCL & SDA lines and it will send ACKDT (see below) bit. This bit will be automatically cleared in hardware. It has application only in master receive mode.

ACKDT: Acknowledge data bit. 1 means not acknowledge and 0 means acknowledge. This value will be transmitted when we set the ACKEN bit (above). This bit has application only in master receive mode.

ACKSTAT: Acknowledge status bit. 1 indicates that acknowledgment was not received from the slave and vice versa. This bit has applications in master transmit mode only.

GCEN: General call enable bit. Setting this bit enables interrupt when a general call address is received in the register SSPSR.

SSPBUF & SSPSR & SSPADD

SSPSR is the shift register used for shifting data in or out. SSPBUF is the buffer register to which data bytes are written to or read from.

SSPADD register holds the slave device address when the SSP is configured in I2C Slave mode. When the SSP is configured in Master mode, the lower seven bits of SSPADD act as the baud rate generator reload value.

In receive operations, SSPSR and SSPBUF together create a double-buffered receiver. When SSPSR receives a complete byte, it is transferred to SSPBUF and the SSPIF interrupt is set.

During transmission, the SSPBUF is not double buffered. A write to SSPBUF will write to both SSPBUF and SSPSR.

Circuit Diagram

LCD:

RS –  RC0

RW – RC1

EN – RC2

Data Lines – Port B

EEPROM:

SDA – RC4

SCL – RC3

PIC16F877A I2C Tutorial

Programming – PIC16F877A I2C Tutorial

In this code, I’m writing “EmbeTronicX” into EEPROM. Then I’m reading the data byte by byte using I2C. After that Displaying the data received from EEPROM. In this project, I divided it into three parts. main.c, LCD.h, I2C.h. If you want to download the full project please Click here.

Main.c

#include<pic.h>
#include"lcd.h"
#include"i2c.h"

__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF & BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);

void delay1();

void main()
{
    lcd_init();
    i2c_init();
    show("  I2C TuTorial  ");
    cmd(0xc2);
    show("Writing...");
    i2c_send_byte(0x0000,"EmbeTronicX");
    delay1();
    cmd(0xc2);
    show("Reading...");
    delay1();
    cmd(0xc2);
    dat(i2c_read_byte(0x0000));
    dat(i2c_read_byte(0x0001));
    dat(i2c_read_byte(0x0002));
    dat(i2c_read_byte(0x0003));
    dat(i2c_read_byte(0x0004));
    dat(i2c_read_byte(0x0005));
    dat(i2c_read_byte(0x0006));
    dat(i2c_read_byte(0x0007));
    dat(i2c_read_byte(0x0008));
    dat(i2c_read_byte(0x0009));
    dat(i2c_read_byte(0x000a));
    
    while(1);
}   

void delay1()
{
    unsigned int j,k;
    for(j=0;j<60000;j++) {
        for(k=0;k<2;k++);
    }
}

I2C.H

#define write_cmd 0xA0
#define read_cmd 0xA1

void i2c_init();
void i2c_start();
void i2c_stop();
void i2c_restart();
void i2c_ack();
void i2c_nak();
void waitmssp();
void i2c_send(unsigned char dat);
void i2c_send_byte(unsigned char addr,unsigned char *count);
unsigned char i2c_read();
unsigned char i2c_read_byte(unsigned char addr);

void i2c_init()
{
    TRISC3=TRISC4=1;
    SSPCON=0x28;                    //SSP Module as Master
    SSPADD=((11059200/4)/100)-1;    //Setting Clock Speed, My PCLK = 11.0592MHz
}

void i2c_start()
{
    SEN=1;
    waitmssp();
}

void i2c_stop()
{
    PEN=1;
    waitmssp();
}

void i2c_restart()
{
    RSEN=1;
    waitmssp();
}

void i2c_ack()
{
    ACKDT=0;
    ACKEN=1;
    waitmssp();
}

void i2c_nak()
{
    ACKDT=1;
    ACKEN=1;
    waitmssp();
}

void waitmssp()
{
    while(!SSPIF);
    SSPIF=0;
}

void i2c_send(unsigned char dat)
{
L1: SSPBUF=dat;
    waitmssp();
    while(ACKSTAT){i2c_restart;goto L1;}
}

void i2c_send_byte(unsigned char addr,unsigned char *count)
{
    i2c_start();
    i2c_send(write_cmd);
    i2c_send(addr>>8);
    i2c_send(addr);
    while(*count) {
        i2c_send(*count++);
    }
    i2c_stop();
}

unsigned char i2c_read()
{
    RCEN=1;
    waitmssp();
    return SSPBUF;
}   

unsigned char i2c_read_byte(unsigned char addr)
{
    unsigned char rec;
L:  i2c_restart();
    SSPBUF=write_cmd;
    waitmssp();
    while(ACKSTAT){goto L;}
    i2c_send(addr>>8);
    i2c_send(addr);
    i2c_restart();
    i2c_send(read_cmd);
    rec=i2c_read();
    i2c_nak();
    i2c_stop();
    return rec;
}

LCD.H

#define rs RC0
#define rw RC1
#define en RC2
#define delay for(i=0;i<1000;i++)

int i;

void lcd_init();
void cmd(unsigned char a);
void dat(unsigned char b);
void show(unsigned char *s);

void lcd_init()
{
    TRISB=TRISC0=TRISC1=TRISC2=0;
    cmd(0x38);
    cmd(0x0c);
    cmd(0x06);
    cmd(0x80);
}

void cmd(unsigned char a)
{
    PORTB=a;
    rs=0;
    rw=0;
    en=1;
    delay;
    en=0;
}

void dat(unsigned char b)
{
    PORTB=b;
    rs=1;
    rw=0;
    en=1;
    delay;
    en=0;
}

void show(unsigned char *s)
{
    while(*s) {
        dat(*s++);
    }
}
        

Output – PIC16F877A I2C Tutorial

PIC16F877A I2C Tutorial

[ Please find the output image Here ]

In our next tutorial, we will see how to use the PIC16F877A Interrupts.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

10 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Table of Contents