Tutorial 8: I2C Communication

Your advanced projects will likely take advantage of additional ICs needed for special functionality. ICs communicate with your Arduino via several protocols, including I2C communication. In this tutorial I show you how to establish communication with a DS3231 Real Time Clock (RTC) via I2C communication.

The purpose of this tutorial isn't to quickly show you how to get your clock up and running. There are perfectly fine libraries out there that will get your clock up and running in just a few minutes. The purpose of this tutorial is to show you how to read the datasheet of a I2C device and write the relevant code to access the full functionality of the device.


DIFFICULTY
MEDIUM
CIRCUITRY KNOWLEDGE
LITTLE
C++ PROGRAMMING
SOME
ABOUT
0
MINUTES
  • How to read a datasheet for an IC
  • How to identify the I2C communication bus on an Arduino
  • Communication conventions with I2C ICs
  • How to program an Arduino to communicate with an I2C device
You can copy / paste the code below if you’re having issues with typos or want a shortcut. However I recommend that you follow along in the tutorial to understand what is going on!


#include <Wire.h>

// 1 Set the time of the clock
// 2 Read the time of the clock
// 3 Set the interrupt pulse rate - SQW - Frequency

#define DS3231_ADDRESS 0x68 
#define DS3231_SECONDS_REG 0x00
#define DS3231_CONTROL_REG 0x0E

static uint8_t convertValueIN(uint8_t value) ;
static uint8_t convertValueOUT(uint8_t value) ;

typedef struct timeParameters {
  uint8_t ss ;
  uint8_t mm ;
  uint8_t hh ;
  uint8_t dy ;
  uint8_t d ;
  uint8_t m ;
  uint8_t y ;
} ;

void setINTFreq(uint8_t freqOption)
{
  /*
   * Option 1: 1Hz
   * Option 2: 1.024kHz
   * Option 3: 4.096kHz
   * Option 4: 8.192kHz
   * Option 5: OFF
   */

   Wire.beginTransmission(DS3231_ADDRESS) ;
   Wire.write(DS3231_CONTROL_REG) ;
   Wire.endTransmission() ;

   Wire.requestFrom(DS3231_ADDRESS, 1) ;
   byte currentConfig = Wire.read() ;

   if (freqOption == 5){
    byte offConfig = currentConfig | 0b00000100 ;

    Wire.beginTransmission(DS3231_ADDRESS) ;
    Wire.write(DS3231_CONTROL_REG) ;
    Wire.write(offConfig) ;
    Wire.endTransmission() ;
   }
   else if (freqOption < 5){
    uint8_t option = freqOption - 1 ;

    byte newConfig = currentConfig & 0b11100111 ;
    newConfig = newConfig & 0b11111011 ;

    newConfig = newConfig | (option << 3) ;

    Wire.beginTransmission(DS3231_ADDRESS) ;
    Wire.write(DS3231_CONTROL_REG) ;
    Wire.write(newConfig) ;
    Wire.endTransmission() ;
   }
   delay(5) ;
}

void setTime(timeParameters *timeVals)
{
  /*
   * From the datasheet
   * 0 - seconds
   * 1 - minutes
   * 2 - hours
   * 3 - day
   * 4 - date
   * 5 - month
   * 6 - year
   */

  Wire.beginTransmission(DS3231_ADDRESS) ;
  Wire.write(DS3231_SECONDS_REG) ;

  Wire.write(convertValueOUT(timeVals->ss)) ;
  Wire.write(convertValueOUT(timeVals->mm)) ;
  Wire.write(convertValueOUT(timeVals->hh)) ;
  Wire.write(0) ;
  Wire.write(convertValueOUT(timeVals->d)) ;
  Wire.write(convertValueOUT(timeVals->m)) ;
  Wire.write(convertValueOUT(timeVals->y)) ;
  Wire.endTransmission() ;
  delay(5) ;
   
}

void readTime(timeParameters *timeVals)
{
  Wire.beginTransmission(DS3231_ADDRESS) ;
  Wire.write(DS3231_SECONDS_REG) ;
  Wire.endTransmission() ;

  Wire.requestFrom(DS3231_ADDRESS, (byte) sizeof(timeParameters)) ;

  timeVals->ss = convertValueIN(Wire.read()) ;
  timeVals->mm = convertValueIN(Wire.read()) ;
  timeVals->hh = convertValueIN(Wire.read()) ;
  Wire.read() ;
  timeVals->d = convertValueIN(Wire.read()) ;
  timeVals->m = convertValueIN(Wire.read()) ;
  timeVals->y = convertValueIN(Wire.read()) ;

  delay(5) ;
}

static uint8_t convertValueIN(uint8_t value)
{
  uint8_t convertedVal = value - 6 * (value >> 4) ;
  return convertedVal ;
}

static uint8_t convertValueOUT(uint8_t value)
{
  uint8_t convertedVal = value + 6 * (value / 10) ;
  return convertedVal ;
}

// ----------------

int timeTrigs = 0 ;

void timeFunc()
{
  timeTrigs += 1 ;
}

timeParameters foo ;

void setup() {
  Serial.begin(9600) ;
  Wire.begin() ;
  delay(2000) ;

  timeParameters example_vals = {
    50,
    59,
    23,
    1,
    31,
    12,
    10,
  } ;

  uint8_t desiredFreq = 4 ;

  setTime(&example_vals) ; // Sets the time
  setINTFreq(desiredFreq) ;
  attachInterrupt(0, timeFunc, RISING) ;

}

void loop() {
  delay(1000) ;
  readTime(&foo) ;

  Serial.print(foo.y) ;
  Serial.print("year ") ;
  Serial.print(foo.m) ;
  Serial.print("month ") ;
  Serial.print(foo.d) ;
  Serial.println("date") ;

  Serial.print(foo.hh) ;
  Serial.print("hour ") ;
  Serial.print(foo.mm) ;
  Serial.print("minutes ") ;
  Serial.print(foo.ss) ;
  Serial.println("seconds") ;

  Serial.print("Trigger count: ") ;
  Serial.println(timeTrigs) ;

  if (timeTrigs >= 8192) {
    timeTrigs = 0 ;
  }

}
You will need to download a few things to get the most out of this tutorial: