I2C is the protocol that we will look at in this post. Since I2C was created by Phillips in the 80’s it has found extensive use in microcontroller based systems. This is because the I2C module only requires two lines to communicate so it is versatile and can be used to allow a master to communicate with a lot of devices.  I2C can be used with displays, sensors, actuators and a host of other devices.

PIC32 I2C Module

The Inter-Integrated Circuit or I2C protocol is a synchronous serial interface that uses only two wires for communication, the Shared Clock Line (SCL) and the Shared Data Line (SDA). Note that for devices to communicate on the I2C bus, they must share a common ground.

I2C like SPI is a master-slave interface with masters controlling slaves connected to the bus. Typically you would have one master connected to the bus and multiple slaves. It should be noted though the protocol allows for multiple masters, though if you have multiple masters bus collisions can take place when two masters try to access the same slave at the same time.

Unlike SPI which is a full-duplex serial communication protocol, I2C is a half-duplex communication protocol and only allows data to be sent or received over the bus. This makes I2C slower than SPI module and the I2C bus operates at 100 kHz in standard mode and 400 kHz in full speed mode. Modern I2C bus speeds can reach as high as over 3 MHz though in most embedded systems operation in standard or full speed mode is most common.

To use the I2c module pull up resistors are required which can service all devices on the bus since the bus drivers are open drain to restore the signal line to high when not being asserted low. Values from 1k to 10k are common, with 10 k resistors recommended for full speed mode and 2.2k resistors recommend for full speed mode. In practice 10k resistors usually do the trick and if they don’t work then you can use 4.7k resistors without any problems and as low as 1k can be used though this is not recommended.

To perform I2C communication the master initializes communication by issuing a start sequence on the bus. Usually for I2C communication to take place, data is transferred during the period the clock line is low. However during the start sequence the masters holds the clock line high and changes the data line to low. When this is done the slaves know that the master is about to initiate communication with a device.

After the start sequence is done the master can transmit data over the bus, it does this by pulling the clock line high then low.

Next the master sends the 7-bit address of the device it wishes to communicate with this is known as the address frame of transmission.

After this data is transferred in a sequence of 8 bits known as the data frames. In I2c you must know that the bits are transferred with the Most Significant Bit or MSB being sent first.  There is a bit which is sent  on the 9th clock pulse which is the Acknowledge (ACK) or Not acknowledge bit (NACK) . The device must pull the SDA line low before the 9th clock pulse or the master will think that an error had occurred and will go to the error handling routine and it this is not possible in practice I hace found that the bus may freeze and the device needs to be restarted.

After the master sends a stop sequence by holding the clock line high while transferring data.

I2C is intended for communication at the board level, though special differential bus extenders are available which can allow the I2C bus to communicate over long distances.

There is also the more advanced 10-bit addressing which is supported by the PIC32MX270F256D, however for most systems being designed this is usually not needed.


Connections

The 24LC16B is connected to the I2C1 bus with 4.7 k pull up resistors.


C Program

/*
 * File: Main.c
 * Author: Armstrong Subero
 * Processor: PIC32MX270F256D w/Ext OSC @ 4MHz, PLL to 48 MHz, 3.3v
 * Program: 11_I2C
 * Compiler: XC32 (v1.44, MPLAX X v4.00)
 * Program Version: 1.0
 *                             
 * Program Description: This Program Allows PIC32MX270F256D to use the I2C
 *                      module to read and write a 24LC16B EEPROM
 * 
 * Hardware Description: An 24LC16B EEPROM is connected to I2C1
 * 
 * Change History:
 *
 * Author             Rev     Date          Description
 * Armstrong Subero   1.0     03/04/2018    Initial Release.
 * 
 *                      
 * Created April 3rd, 2018, 8:02 PM
 */

/*******************************************************************************
 Includes and defines
*******************************************************************************/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "PIC32MX270_STD.h"
#include "Delay.h"
#include "uart1.h"
#include "i2c.h"
#include <string.h>

#define LED LATBbits.LATB14


/*******************************************************************************
* Function Name: void initMain (void)
*
* Specification: initializes main
*
*******************************************************************************/
void initMain(void)
{  
    uc_init();                  // initialize microcontroller
    TRISBbits.TRISB14 = 0;      // set PINB1 as output
    
    // unlock PPS
    systemReg_Unlock();
    CFGCONbits.IOLOCK = 0;

    RPC5Rbits.RPC5R = 0x0001;   //RC5->UART1 TX

    // lock PPS
    CFGCONbits.IOLOCK = 1; 
    systemReg_Lock();
    
    i2c_master_setup();        // initialize master I2C
    UART1_Initialize();        // initialize UART
}


/*******************************************************************************
* Function Name: void main (void)
*
* Specification: main function
*
*******************************************************************************/
void main(void) {
    
    initMain();                         // initializes main function
    __XC_UART = 1;                      // configure printf to use UART1
      
    unsigned int incoming_data;         // variable to store read value
   
    while(1)
    {
      printf("Write begin\n");           // start write
      i2c_master_start();                // issue I2C start signal
      i2c_master_send(0xA2);             // send byte via I2C  (device address + W)
      i2c_master_send(2);                // send byte (address of EEPROM location)
      i2c_master_send(0x01);             // send data (data to be written)
      i2c_master_stop();                 // issue I2C stop signal
      delay_ms(100);                     // wait 100 ms
      printf("Write end\n");             // end write
      
      
      printf("Read begin\n");            // start read
      i2c_master_start();                // issue I2C start signal
      i2c_master_send(0xA2);             // send byte via I2C  (device address + W)
      i2c_master_send(2);                // send byte (data address)
      i2c_master_restart();              // issue I2C signal repeated start
      i2c_master_send(0xA3);             // send byte (device address + R)
      incoming_data = i2c_master_recv(); // Read the data
      i2c_master_ack(1);                 // send NACK
      i2c_master_stop();                 // issue I2C stop signal 
      printf("Read end\n");              // end read
 
    
      printf("%d\n", incoming_data);     // print read value
      delay_ms(1000);                    // delay 1 second
    }
}

We initially write “0x01” to the EEPROM, then read it back and send the result read over the UART.


Link to Project

You can download the entire project here:

I2C Project

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *