Saturday, November 21, 2020

0000 0000 0100 1010

PCF8563 Time/Date IC

Slightly obsessed with timing devices (really?!), I saw these PCF8563 real time clock chips that had so many features and looked too good to be true (and so cheap!). I grabbed some that were already made up in modules, and others that are just raw IC chips ready to be used in an appropriate circuit.

In this blog I am going to focus on the raw IC version and try to get it going so that I can measure and store time.

The first order of business is to solder up the SOP8 package onto a DIP8 adapter. I put some extra white paint on the little fella so that I know it's not an AVR or PADAUK when I reach for it!


The plan is to put this on a breadboard and then:

  1. program an Arduino Nano to push out the time/date to the RTC
  2. everything sleeps in low current mode
  3. pushing a button wakes up the Nano
  4. the Nano polls the RTC for time and date (and turns on an LED for good measure, also who doesn't like a blinking LED?)
  5. the Nano pushes out the time/date to an OLED display for a few seconds
  6. everyone goes back to sleep
  7. battery backup the whole shebang with a coin cell battery?
  8. swap out the Nano for an ATMega328 to further reduce the current requirements?

I am more than a little unhappy with the internet over this little circuit, for a couple of reasons. Firstly when I looked for a likely setup, I came across this site with the following picture.


Even though from previous experience with crystals I knew that capacitors need to be used to get the things oscillating, the only capacitor mentioned on the page is for decoupling and it's not near the crystal.

Upshot? I spent a "happy" couple of hours trying to debug code that clearly showed the Nano talking to the RTC, and vice versa, but the RTC not "ticking" in any clock-like manner. This problem was eventually solved with the addition of a 20pf capacitor between the crystal and ground. Do I need to put one on the other side? I'm not sure! At this point I was just happy to see the little fella measuring the <frustrating long> passage of time.


Secondly there were a LOT of libraries purporting to help communicate with the PCF8563. Sadly many had holes and it took quite a bit of fiddling to realise that this library is the easiest and most functional. It's found and installed very quickly using the Arduino IDE "Library Manager" dialog.

I also realised that for my sanity to prevail I would work with two lots of (very similar) code, one to set the RTC and one to then read it on demand. I have seen these combined on some sites with similar software via a "flag" or test, but it seemed just as easy to separate out these similar but fundamentally different functions, even though much of the rest of the code is the same.

I've highlighted the essential difference.

Setting Code:

/*
   Using an PCF8563 as an RTC for an Arduino Nano
   and an OLED display

   One Circuit Friday 13 November 22:26:09 AEDT 2020
   Friday 13th! Yikes!
*/

#include <Wire.h>
#include "RTClib.h"
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// some variables and constants
byte thehour = 0;
byte theminute = 0;
byte thesecond = 0;
byte theday = 0;
byte themonth = 0;
int theyear = 0;
String myminute = "minute";
String mysecond = "second";
const byte ledPin = 4;
const byte interruptPin = 2;
volatile boolean button = false;

#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// initialise our I2C devices
RTC_PCF8563 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// button ISR
void wakeUpNow() {
  button = true;
}

// sleep routine
void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
  sleep_enable();         
  sleep_mode(); 
  sleep_disable(); 
  detachInterrupt(0);
}

void setup () {
  while (! rtc.begin()) {
    delay(500);
  }
  delay(1000); // settle rtc
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.display();
  delay(3000);
  display.clearDisplay();
  display.display();
}

void showtime() {
  digitalWrite(ledPin, HIGH);
  DateTime now = rtc.now();
  thehour = now.hour();
  theminute = now.minute();
  // make it pretty if the numbers are below 10
  if (theminute < 10) {
    myminute = "0" + String(theminute);
  }
  else {
    myminute = String(theminute);
  }
  thesecond = now.second();
  if (thesecond < 10) {
    mysecond = "0" + String(thesecond);
  }
  else {
    mysecond = String(thesecond);
  }
  theday = now.day();
  themonth = now.month();
  theyear = now.year();
  // string together the date/time (pun intended)
  String nowtime = "TIME " + String(thehour) + ":" + myminute + ":" + mysecond;
  String nowdate = "DATE " + String(theday) + "/" + String(themonth) + "/" + String(theyear);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.setCursor(10, 15);
  display.println(nowtime);
  display.setCursor(10, 25);
  display.println(nowdate);
  display.display();
  delay(5000);
  digitalWrite(ledPin, LOW);
  display.clearDisplay();
  display.display();
  button = false;
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
}

void loop() {
  if (button) {
    showtime();
  }
  sleepNow();
}

Reading Code:

/*
   Using an PCF8563 as an RTC for an Arduino Nano
   and an OLED display

   One Circuit Friday 13 November 22:26:09 AEDT 2020
   Uh Oh - Friday 13th...
*/

#include "RTClib.h"
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// some variables and constants
byte thehour = 0;
byte theminute = 0;
byte thesecond = 0;
byte theday = 0;
byte themonth = 0;
int theyear = 0;
String myminute = "minute";
String mysecond = "second";
const byte ledPin = 4;
const byte interruptPin = 2;
volatile boolean button = false;
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// initialise our I2C devices
RTC_PCF8563 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// button ISR
void wakeUpNow() {
  button = true;
}

// sleep routine
void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
  sleep_disable();
  detachInterrupt(0);
}

void setup () {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  while (! rtc.begin()) {
    delay(500);
  }
  delay(1000); // settle rtc
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
  DateTime now = rtc.now();
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.display();
  delay(3000);
  display.clearDisplay();
  display.display();
}

void showtime() {

  digitalWrite(ledPin, HIGH);
  DateTime now = rtc.now();
  thehour = now.hour();
  theminute = now.minute();
  // make it pretty if the numbers are below 10
  if (theminute < 10) {
    myminute = "0" + String(theminute);
  }
  else {
    myminute = String(theminute);
  }
  thesecond = now.second();
  if (thesecond < 10) {
    mysecond = "0" + String(thesecond);
  }
  else {
    mysecond = String(thesecond);
  }
  theday = now.day();
  themonth = now.month();
  theyear = now.year();
  // string together the date/time (pun intended)
  String nowtime = "TIME " + String(thehour) + ":" + myminute + ":" + mysecond;
  String nowdate = "DATE " + String(theday) + "/" + String(themonth) + "/" + String(theyear);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.setCursor(10, 15);
  display.println(nowtime);
  display.setCursor(10, 25);
  display.println(nowdate);
  display.display();
  delay(5000);
  digitalWrite(ledPin, LOW);
  display.clearDisplay();
  display.display();
  button = false;
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
}

void loop() {
  if (button) {
    showtime();
  }
  sleepNow();
}

After all of this fiddling about I was able to breadboard the devices together and make the clock a reality.


The battery backup proved useful for kicking out the programming USB cable as the power supply and running the device independently via a battery bank, with a coin cell battery stuck in to keep the clock ticking.

For backup power to work properly I needed to put in a diode (1N4148 in my case) from the 5V to the IC and another one from the battery (3.3V) to the IC. I'm pretty sure a Schottky would be fine as well for this job. The diodes prevent the backup from trying to run the whole circuit when the power is out, and also to prevent the 5V from the main battery/power source from interfering with the coin cell.

Just for "fun" and as seen in the picture above, I also compiled the code for the ATMega328 which worked fine - nice one!





No comments:

Post a Comment