I2C RTC PCF8563: basic usage with Arduino

EDIT: this blog page was edited on September 24th by adding the register organization table and by reversing the order of day and weekday reading in the code (you have to read the day before of the weekday)

In this article I will show how to:

  • connect PCF8563 to Arduino
  • set date & time
  • get date & time with, hopefully, an optimized code
  • modify Wire library to run at 400 kbit/s (fast mode) instead of the standard 100 kbit/s speed

Hardware

PCF8563 is an I2C real time clock and calendar capable of working at I2C fast mode (400 kbit/s) that is the fastest I2C mode that Atmel ATmega328 (and hence Arduino) can do.

From PCF8563 datasheet:

The PCF8563 is a CMOS Real-Time Clock (RTC) and calendar optimized for low power consumption.
A programmable clock output, interrupt output, and voltage-low detector are also provided.
All addresses and data are transferred serially via a two-line bidirectional I2C-bus.
Maximum bus speed is 400 kbit/s. The register address is incremented automatically after each written or read data byte.

RTCs use registers to store pieces of information; this table describes register organization of PCF8563:

This is the PIN configuration of PCF8563 version P (DIP8 package – from PCF8563 datasheet):

Where:

  • OSCI = oscillator input
  • OSCO = oscillator output
  • INT = interrupt output (open-drain; active LOW)
  • VSS = ground
  • SDA = serial data input and output
  • SCL = serial clock input
  • CLKOUT = clock output, open-drain
  • VDD = supply voltage

As you can see, PCF8563 needs an external 32.768 kHz oscillator (quartz) to work, so we need to get one.

It is a good practice to use pull-up resistors on the I2C bus lines, so we need to get a couple of 10.000 Ω resistors.

Finally, since PCF8563 offers the possibility to output a programmable square wave on CLKOUT pin, we will connect a led (with a 470 ohm resistor) that will notify us, by blinking, that the RTC is correctly connected and initialized.

So the complete circuit, sketched with LibreOffice in Ubuntu, is:

xxxxxx

PCF8563 connected to Arduino

Different colors of lines have different meanings:

  • Black = lines connected to ground
  • Red = lines connected to supply voltage (5V)
  • Blue = I2C bus lines
  • Yellow = lines that connect the oscillator to the RTC

Finally, this is the real circuit (for practical reason I did not connect pull-up resistors, but you should do it):

Software

Please consider these aspects when looking at the code:

  • the I2C address of the RTC has to be a 7 bit address: the eighth bit is not needed since it’s used to indicate if you are reading or writing to the device (take a look here)
  • I wanted to build an optimized code, in a way that Arduino ask to the RTC only pieces of information that changed: this way the code can be added to an existing complex application without breaking anything (hopefully). This means:
    • I repeatedly ask the RTC for seconds
    • When seconds change, I ask the RTC for minutes only when seconds become equal to zero
    • When minutes change, I ask the RTC for hours only when minutes become equal to zero
    • When hours change, I ask the RTC for the remaining pieces of information (weekday, day, month, century and year) only when hours become equal to zero
  • since RTC manage numbers in BCD format, functions to convert BCD (binary coded decimal) to decimal and viceversa had to be written
  • I decided to create a register of errors and an error routine so that in case of any failure an error led will start blinking a number of times corresponding to the error number
  • just for debugging, the code displays only when needed time and date on the serial monitor
  • the RTC starts counting from 23.59.53 – December 31st, 2011

This is the code:

#include <Wire.h>//I2C header file

// Defines
//#define DEBUG // Uncomment to turn on verbose mode
#define I2C_RTC 0x51 // 7 bit address (without last bit - look at the datasheet)
#define ERROR_LED 13

// Errors
#define ERROR_RTC_SET 1 // Unable to set RTC time and date
#define ERROR_RTC_GET 2 // Unable to get RTC time and date
#define ERROR_CLOCK_INTEGRITY 3 // RTC clock integrity not guaranteed

// Global variables
byte result;
byte second;
byte second_old; // The code ask the RTC for data only when the previous value has changed
byte minute;
byte minute_old; // The code ask the RTC for data only when the previous value has changed
byte hour;
byte hour_old; // The code ask the RTC for data only when the previous value has changed
byte weekday;
byte day;
byte month;
byte year;
byte century;
char* weekdayname[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

// Function prototypes
byte BcdToDec(byte);
byte DecToBcd(byte);
void SetError(int);

void setup()
{
  pinMode(ERROR_LED, OUTPUT); // Set error LED
  Wire.begin(); // Initiate the Wire library and join the I2C bus as a master
  Serial.begin(9600); // Initiate serial communication

// Set initial date and time
  second_old = second = 53; // Second (0-59)
  minute_old = minute = 59; // Minute (0-59)
  hour_old = hour = 23; // Hour (0-23)
  weekday = 0; // Day of the week (0-6)
  day = 31; // Day (1-31)
  month = 12; // Month (1-12)
  year = 11; // Year (0-99)
  Wire.beginTransmission(I2C_RTC); // Select RTC
  Wire.send(0);        // Start address
  Wire.send(0);     // Control and status 1
  Wire.send(0);     // Control and status 2
  Wire.send(DecToBcd(second));     // Second
  Wire.send(DecToBcd(minute));    // Minute
  Wire.send(DecToBcd(hour));    // Hour
  Wire.send(DecToBcd(day));    // Day
  Wire.send(DecToBcd(weekday));    // Weekday
  Wire.send(DecToBcd(month));     // Month (with century bit = 0)
  Wire.send(DecToBcd(year));    // Year
  Wire.send(0b10000000);    // Minute alarm (and alarm disabled)
  Wire.send(0b10000000);    // Hour alarm (and alarm disabled)
  Wire.send(0b10000000);    // Day alarm (and alarm disabled)
  Wire.send(0b10000000);    // Weekday alarm (and alarm disabled)
  Wire.send(0b10000011);     // Output clock frequency enabled (1 Hz)
  Wire.send(0);     // Timer (countdown) disabled
  Wire.send(0);     // Timer value
  Wire.endTransmission();
  result = Wire.endTransmission();

#ifdef DEBUG
  Serial.print("Result of setting date and time: ");
  Serial.println(result, DEC);
#endif

  if (result) SetError(ERROR_RTC_SET);
}

void loop()
{
  Wire.beginTransmission(I2C_RTC);
  Wire.send(0x02); // Start address
  result = Wire.endTransmission();
#ifdef DEBUG
  Serial.print("Result of asking for date and time: ");
  Serial.println(result, DEC);
#endif
  if (result) SetError(ERROR_RTC_GET);

  Wire.requestFrom(I2C_RTC, 1);
  second = Wire.receive();
  if (second & 0x80) SetError(ERROR_CLOCK_INTEGRITY);
  second = BcdToDec(second & 0b01111111);
  if (second != second_old) // Cycle begins only when it has changed
  {
    second_old = second;
    if (second == 0) // If second is zero I need to ask for the minute
    {
      Wire.requestFrom(I2C_RTC, 1);
      minute = BcdToDec(Wire.receive());
      if (minute != minute_old) // Cycle begins only when it has changed
      {
        minute_old = minute;
        if (minute == 0) // If minute is zero I need to ask for the hour
        {
          Wire.requestFrom(I2C_RTC, 1);
          hour = BcdToDec(Wire.receive());
          if (hour != hour_old) // Cycle begins only when it has changed
          {
            hour_old = hour;
            if (hour == 0) // If hour is zero I need to ask for other elements
            {
              Wire.requestFrom(I2C_RTC, 4);
              day = BcdToDec(Wire.receive());
              weekday = BcdToDec(Wire.receive());
              month = Wire.receive();
              century = (month & 0x80);
              month = BcdToDec(month & 0b01111111);
              year = BcdToDec(Wire.receive());
            }
          }
        }
      }
    }
    Serial.print(weekdayname[weekday]);
    Serial.print("(");
    Serial.print(weekday, DEC);
    Serial.print(")");
    if (century)
      Serial.print(" 21");
    else
      Serial.print(" 20");
    if (year < 10) Serial.print("0");
    Serial.print(year, DEC);
    Serial.print("-");
    if (month < 10) Serial.print("0");
    Serial.print(month,DEC);
    Serial.print("-");
    if (day < 10) Serial.print("0");
    Serial.print(day, DEC);
    Serial.print("");
    if (hour < 10) Serial.print("0");
    Serial.print(hour,DEC);
    Serial.print(":");
    if (minute < 10) Serial.print("0");
    Serial.print(minute, DEC);
    Serial.print(":");
    if (second < 10) Serial.print("0");
    Serial.println(second, DEC);
  }
}

// Converts a BCD (binary coded decimal) to decimal
byte BcdToDec(byte value)
{
  return ((value / 16) * 10 + value % 16);
}

// Converts a decimal to BCD (binary coded decimal)
byte DecToBcd(byte value){
  return (value / 10 * 16 + value % 10);
}

void SetError(int error) // Blinks forever the error led a number of times corresponding to error number
{
  while(1) // Forever
  {
    for (byte index = 0; index < error; index++)
    {
      digitalWrite(ERROR_LED, HIGH);
      delay(500);
      digitalWrite(ERROR_LED, LOW);
      delay(500);
    }
    delay(1000);
  }
}

Modify Wire library to run at I2C fast mode

These are the steps to run with Arduino an I2C device at fast mode:

  1. Open header file twi.h from Arduino Wire library (in Ubuntu it is located in /usr/share/arduino/libraries/Wire/utility/)
  2. Near the top of the file look for the portion of code that define I2C speed:
    #ifndef TWI_FREQ
    #define TWI_FREQ 100000L
    #endif
  3. Change it in:
    #ifndef TWI_FREQ
    #define TWI_FREQ 400000L
    #endif
  4. Recompile your sketch and you’re done: it is not necessary to delete object file Wire.o and twi.o since, starting from version 0017, Arduino IDE compiles and links libraries on the fly

The final result

This short video shows the RTC working (the yellow led is blinking, that means that the RTC is running); look at what happens when I detach one of the I2C lines…

Italian style

Mi è sempre piaciuto volare, anche quando per necessità mi è capitato di volare settimanalmente in Italia (e per un breve periodo anche in Europa).

Ho recentemente preso un volo Alitalia per una breve vacanza al mare.

Circa un’ora dopo l’orario previsto (ma la notizia non è questa), siamo finalmente saliti a bordo e la situazione che mi si è presentata era, diciamo, sfortunatamente singolare e ho deciso di documentarla (per un attimo mi sono anche chiesto se non mi fosse convenuto scendere…).

Agli aruspici del cielo l’interpretazione dei segni: quale sarà il futuro di Alitalia?

Il bracciolo alla mia sinistra

Il bracciolo alla mia destra

Un sedile alla mia sinistra

Un pezzo del bracciolo di destra (diligentemente consegnato alla hostess)

Cactus flowers

As it’s happening once a year since few years, a couple of nights ago my cactus, that’s something like 8 years old, flowered and three big and wonderful flowers exploded.

You can see one of them just below: they are big (diameter of about 20 cm) white on the front and pink on the back: don’t you think they are very nice?

Let’s tell the story of this cactus: in 2003 when I was working for a bank in Rome my friend Carlo, who was working for the bank, gave me as a present the top of his old cactus that was in his office: he was moving and, instead of thrashing the cactus, he cut the top of the plant (the first 10 cm) and gave it to me since I told him that I was looking for a plant for my flat.

I was sure that the short piece of cactus would never have survived such a raw treatment, and indeed for the first 2 years nothing happened: it was standing still! I thought it was dead, but I didn’t through it away because it still was green…

Suddenly, when I moved from Rome to the north of Italy in 2005, the cactus began to grow at a fast pace (even half a meter each year) and now it is taller then me!

Furthermore, since three years every summer (in july or august) it flowers!

Flowers last no more than one day, after which they wither and, after other few days, they detach from the plant and fall down together with their supporting arm.

Please take a look to the short gallery of pictures below: I hope you like it.

Ciao

Post scriptum: all pictures are taken with my N900.

The top of the cactus and two flowers

The three flowers

Detail of cactus flower

The cactus flowers (rear view)

Another view of a cactus flower

The cactus and his flowers

The whole cactus with withered flowers the morning after

Arduino hack day in Milan

I had the pleasure to join on the 19th of June the second day of the Milan Arduino Camp: the hack day.

There were six groups (composed by less than ten persons each) which had the goal to build in one day a working robot capable of avoiding obstacles.

Pieces were mostly (but not only) extracted from broken printers (motors, transistors) and the heart of the robot obviously was an Arduino board (in our case an Arduino Uno).

Although not perfectly working at the end of the day (due to time constraint), our Cicciobot (“ciccio” means fat in italian) placed second in the final standings.

The Cicciobot

It has been a fun and very instructive experience, albeit very short and stressful (everyone was aiming at the first place!).

And I had the possibility to phisically know people since that moment known only by their nicknames…