Pedro Cruz / PCF8583_rtc

Fork of PCF8583_rtc by Dennis Smith

Files at this revision

API Documentation at this revision

Comitter:
dennyem
Date:
Fri Feb 14 00:21:38 2014 +0000
Child:
1:eaba89d6e5d8
Commit message:
Made into a library

Changed in this revision

PCF8583_rtc.cpp Show annotated file Show diff for this revision Revisions of this file
PCF8583_rtc.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PCF8583_rtc.cpp	Fri Feb 14 00:21:38 2014 +0000
@@ -0,0 +1,392 @@
+/**
+ ********************************************************************************
+ * An mbed class to control the PCF8583 Real time Clock/Calender
+ * Copyright (c) 2014 Dennis (Denny) Smith - dennyem
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * 13.01.14    initial design for PIC devices using PICC compiler
+ * 21.01.14    ported to mBed LPC1768 in 'C'
+ * 09.02.14    converted to C++ and ported to mBed LPC812
+ *
+ * TODO
+ *       FormatDateTime needs am/pm, 12/24 hour parsing
+ *       Alarm functions not yet implemented
+*/
+
+#include <mbed.h>
+#include <PCF8583_rtc.h>
+
+//-----------------------------------------------------------------------------
+// constructor -- accepts an I2c object to use for connection with the rtc
+PCF8583rtc::PCF8583rtc(I2C *i2c, char I2cAddress)
+{
+   _i2c = i2c;
+   _I2cAddress = I2cAddress;
+   
+  configureControlReg(0);
+      
+  ShortDateFormat = "d,m,yy";
+  LongDateFormat  = "dddd dd mmmm yyyy";
+  ShortTimeFormat = "d:m:yy";
+  LongTimeFormat  = "dd:nn:yyyy";
+
+  DateSeparator   = '/';
+  TimeSeparator   = ':';
+
+  ShortDayNames[0] = "Mon";
+  ShortDayNames[1] = "Tue";
+  ShortDayNames[2] = "Wed";
+  ShortDayNames[3] = "Thu";
+  ShortDayNames[4] = "Fri";
+  ShortDayNames[5] = "Sat";
+  ShortDayNames[6] = "Sun";
+
+  LongDayNames[0]  = "Monday";
+  LongDayNames[1]  = "Tuesday";
+  LongDayNames[2]  = "Wednesday";
+  LongDayNames[3]  = "Thursday";
+  LongDayNames[4]  = "Friday";
+  LongDayNames[5]  = "Saturday";
+  LongDayNames[6]  = "Sunday";
+
+  ShortMonthNames[0]  = "Jan";
+  ShortMonthNames[1]  = "Feb";
+  ShortMonthNames[2]  = "Mar";
+  ShortMonthNames[3]  = "Apr";
+  ShortMonthNames[4]  = "May";
+  ShortMonthNames[5]  = "Jun";
+  ShortMonthNames[6]  = "Jul";
+  ShortMonthNames[7]  = "Aug";
+  ShortMonthNames[8]  = "Sep";
+  ShortMonthNames[9]  = "Oct";
+  ShortMonthNames[10] = "Nov";
+  ShortMonthNames[11] = "Dec";
+
+  LongMonthNames[0]   = "January";
+  LongMonthNames[1]   = "February";
+  LongMonthNames[2]   = "March";
+  LongMonthNames[3]   = "April";
+  LongMonthNames[4]   = "May";
+  LongMonthNames[5]   = "June";
+  LongMonthNames[6]   = "July";
+  LongMonthNames[7]   = "August";
+  LongMonthNames[8]   = "September";
+  LongMonthNames[9]   = "October";
+  LongMonthNames[10]  = "November";
+  LongMonthNames[11]  = "December";
+};
+
+void PCF8583rtc::write(const char address, struct DateTime_t dti)
+{
+    char tmp[8];
+
+    pauseCounting();    //Must stop counting before initialising Date/time
+
+    tmp[0] = address;                                               // Address is 1 for Time or 10 for Alarm
+    
+    //Values must be in BCD form
+    tmp[1] = dti.time.hundreds;                                     // Hundredths of a second
+    tmp[2] = dti.time.seconds;                                      // Seconds
+    tmp[3] = dti.time.minutes;                                      // Minutes
+    tmp[4] = dti.time.hours;                                        // Hours
+    tmp[5] = dti.date.day & 0x3F;                                   // Always set the 3 year bits to 0
+
+    if(address == TIME)
+        tmp[6] = (((dti.date.weekday & 7) << 5 ) | dti.date.month); // Weekday/month
+    else
+        tmp[6] = dti.date.month & 0x1f;                             // No Weekday for alarm
+
+    _i2c->write(_I2cAddress, tmp, 7);                              // Address PCF8583, see PCF8583 datasheet
+    _i2c->stop();
+
+    if(address == TIME) {
+        writeByte(CENTURY_REG, dti.date.century);                   // Store the full 4 digit year in NV Ram
+        writeByte(YEAR_REG, dti.date.year);
+    };
+
+    enableCounting();
+};
+
+//--------------------- Reads time and date information from RTC (PCF8583)
+struct DateTime_t PCF8583rtc::read(const char address)
+{
+    char tmp[8];
+    char year_bits = 0;                   // To test for year change
+
+    tmp[0] = address;
+    _i2c->write(_I2cAddress, tmp, 1);     // Address PCF8583, see PCF8583 datasheet
+    _i2c->read(_I2cAddress | 1, tmp, 6);  // Address PCF8583 for reading R/W=1
+    _i2c->stop();
+    
+    dt.time.hundreds   = tmp[0];
+    dt.time.seconds    = tmp[1];
+    dt.time.minutes    = tmp[2];
+    dt.time.hours      = tmp[3] & 0x3F;
+    dt.time.fmt_hours  = tmp[3] & 0x80;   // 12/24 hour format
+    dt.time.am_pm_flag = tmp[3] & 0x40;   // Am/Pm flag
+    dt.date.day        = tmp[4] & 0x3F;   // Day of the Month
+    dt.date.month      = tmp[5] & 0x1F;
+
+    if(address == TIME)
+        year_bits = (tmp[4] & 0xC0) >> 6;
+    else
+        dt.date.year = 0;                                              // No year for alarm
+
+
+    if(address == TIME)
+        dt.date.weekday =  tmp[5] >> 5;
+    else
+        dt.date.weekday =  0;                                          // No weekday for alarm
+
+    if(address == TIME) {
+        tmp[0] = readByte(CENTURY_REG);
+        dt.date.century  = ((tmp[0] & 0xF0) >> 4) * 10 + (tmp[0] & 0x0F);
+        tmp[0] = readByte(YEAR_REG);
+        dt.date.year  = ((tmp[0] & 0xF0)  >> 4) * 10 + (tmp[0] & 0x0F);
+
+        if(year_bits > 0) {                                            // Midnight on new years eve?
+            dt.date.year += 1;                                         // Increment the year
+            writeByte(YEAR_REG, dt.date.year);                         // Save the new year value to NV Ram
+            writeByte(5, dt.date.day & 0x3F);                          // Clear the year bits but preserve the date
+        }
+    }
+   
+    return dt;
+};
+
+void PCF8583rtc::FormatDateTime(char *dest, char *f)
+{
+    int i;
+    
+    if(f != 0 && *f != 0) {   //If the format param is empty then do a default 'c'
+        while(*f != 0) {        //expect null terminated string (we hope)
+            switch(*f) {
+                case 'c':
+                    break;
+                case 'd':
+                    if(*(f+1) != 'd') {            //'d' - Day with no leading zero
+                        dest += Bcd2Char(dest, dt.date.day, false);
+                    } else {
+                        f++;
+                        if(*(f+1) != 'd')            //'dd' - Day with leading zero
+                            dest += Bcd2Char(dest, dt.date.day, true);
+                        else {
+                            f++;
+                            if(*(f+1) != 'd') {        //'ddd' - Short day name
+                                i = 0;
+                                while(ShortDayNames[dt.date.weekday][i] != 0)
+                                    *dest++ = ShortDayNames[dt.date.weekday][i++];
+                            } else {
+                                f++;
+                                i = 0;
+                                while(LongDayNames[dt.date.weekday][i] != 0)
+                                    *dest++ = LongDayNames[dt.date.weekday][i++];
+                            }
+                        }
+                    }
+                    break;
+                case 'm':
+                    if(*(f+1) != 'm') {            //'m' - Month with no leading zero
+                        dest += Bcd2Char(dest, dt.date.month, false);
+                    } else {
+                        f++;
+                        if(*(f+1) != 'm')            //'mm' - Month with leading zero
+                            dest += Bcd2Char(dest, dt.date.month, true);
+                        else {
+                            f++;
+                            if(*(f+1) != 'm') {        //'mmm' - Short month name
+                                i = 0;
+                                while(ShortMonthNames[dt.date.month - 1][i] != 0)
+                                    *dest++ = ShortMonthNames[dt.date.month - 1][i++];
+                            } else {
+                                f++;
+                                i = 0;
+                                while(LongMonthNames[dt.date.month - 1][i] != 0)
+                                    *dest++ = LongMonthNames[dt.date.month - 1][i++];
+                            }
+                        }
+                    }
+                    break;
+                case 'y':
+                    if(*(f+1) == 'y') {
+                        f++;                                 //We have at least a 'yy'
+                        if(*(f+1) == 'y' && *(f+2) == 'y') { //'yyyy' - 4 digit year
+                            dest += Bcd2Char(dest, dt.date.century, true);
+                            f += 2;
+                        }
+                        dest += Bcd2Char(dest, dt.date.year, true);
+                    }
+                    break;
+                case 'h':
+                    if(*(f+1) != 'h') {            //'h' - Hour with no leading zero
+                        dest += Bcd2Char(dest, dt.time.hours, false);
+                    } else {
+                        f++;
+                        dest += Bcd2Char(dest, dt.time.hours, true);
+                    }
+                    break;
+                case 'n':
+                    if(*(f+1) != 'n') {            //'m' - Minutes with no leading zero
+                        dest += Bcd2Char(dest, dt.time.minutes, false);
+                    } else {
+                        f++;
+                        dest += Bcd2Char(dest, dt.time.minutes, true);
+                    }
+                    break;
+                case 's':
+                    if(*(f+1) != 's') {            //'s' - Seconds with no leading zero
+                        dest += Bcd2Char(dest, dt.time.seconds, false);
+                    } else {
+                        f++;
+                        dest += Bcd2Char(dest, dt.time.seconds, true);
+                    }
+                    break;
+                case 'z':
+                    if(*(f+1) != 'z') {            //'z' - Hundredths with no leading zero
+                        dest += Bcd2Char(dest, dt.time.hundreds, false);
+                    } else {
+                        f++;
+                        dest += Bcd2Char(dest, dt.time.hundreds, true);
+                    }
+                    break;
+                case '/':
+                    *dest++ = DateSeparator;
+                    break;
+                case ':':
+                    *dest++ = TimeSeparator;
+                    break;
+                case 39 :
+                    while(*++f != 0 && *f != 39) *dest++ = *f;
+                    break;  //Ignore the first '
+                default:
+                    *dest++ = *f;
+                    break;            //Anything we don't recognise, return it
+            }
+            f++;
+        }
+    }
+    *dest = 0;    //Null terminate
+};
+
+bool PCF8583rtc::WriteNVram(char address, char *value, char num)
+{
+    char buf[252];
+    
+    buf[0] = address;
+    memcpy(&buf[1], value, num);
+    
+    if((address < USER_REG) || (num == 0))   // dont allow overwriting first 2 bytes
+        return false;
+    
+    _i2c->write(_I2cAddress, buf, num + 1);  // write the data
+    _i2c->stop();
+
+    return true;
+};
+
+bool PCF8583rtc::ReadNVram(char address, char * dest, char num)
+{
+    char    buf[2];
+    
+    if((address < USER_REG) || (num == 0))     // dont allow overwriting first 2 user bytes
+        return false;
+      
+    buf[0] = address;
+    _i2c->write(_I2cAddress, buf, 1);          // set the rom address
+    _i2c->read(_I2cAddress | 1, dest, num);    // read the data
+    _i2c->stop();
+    
+    return true;
+};
+
+/*****************************************************************************/
+/************************** Private Functions ********************************/
+/*****************************************************************************/
+
+char PCF8583rtc::Bcd2Char(char *d, char val, char WantLeadZero)
+{
+    char  n = 0;
+
+    if(WantLeadZero == true || (val / 10) != 0) {
+        *d++ = (val / 10) + 48;
+        n++;
+    }
+    *d = (val % 10) + 48;
+    return(n + 1);
+}
+
+//----------------------------------------------
+// This function converts an 8 bit binary value to a 1 byte BCD value.
+// The input range must be from 0 to 99.
+char PCF8583rtc::bin2bcd(char value)
+{
+    int tmp = 0;
+
+    while(1) {
+        // Get the tens digit by doing multiple subtraction
+        // of 10 from the binary value.
+        if(value >= 10) {
+            value -= 10;
+            tmp += 0x10;
+        } else {  // Get the ones digit by adding the remainder.
+            tmp += value;
+            break;
+        }
+    }
+    return tmp;
+}
+
+void PCF8583rtc::configureControlReg(char control)
+{
+    writeByte(0, control);
+}
+
+void PCF8583rtc::configureAlarmReg(char alarm)
+{
+    writeByte(0x08, alarm);
+}
+
+void PCF8583rtc::writeByte(char address, char d)
+{
+    char buf[2];
+
+    buf[0] = address;
+    buf[1] = d;
+    _i2c->write(_I2cAddress, buf, 2);                              // Address PCF8583, see PCF8583 datasheet
+    _i2c->stop();
+}
+
+char PCF8583rtc::readByte(char address)
+{
+    char buf[2];
+
+    buf[0] = address;
+    _i2c->write(_I2cAddress, buf, 1);
+    _i2c->read(_I2cAddress | 1, buf, 1);
+    _i2c->stop();
+
+    return buf[0];
+}
+
+void PCF8583rtc::pauseCounting()
+{
+    char tmp;
+    tmp = readByte(0);
+    tmp = tmp | 0x80;
+    writeByte(0, tmp);
+}
+
+void PCF8583rtc::enableCounting()
+{
+    char tmp;
+    tmp = readByte(0);
+    tmp = tmp ^ 0x80;
+    writeByte(0, tmp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PCF8583_rtc.h	Fri Feb 14 00:21:38 2014 +0000
@@ -0,0 +1,217 @@
+#include <mbed.h>
+
+#define PCF8583_addr_1      0xA0     // PCF8583 Write address,
+#define PCF8583_addr_2      0xA2     // PCF8583 Write address,
+#define ON                  1
+#define OFF                 0
+
+//Configuration Alarm Control Register
+#define alarm_enable_bit    0x04
+#define alarm_flag          0x02;
+#define timer_hundsek       0x01
+#define timer_seconds       0x02
+#define timer_minutes       0x03
+#define timer_hours         0x04
+#define timer_days          0x05
+#define timer_int_enable    0x08
+#define daily_alarm         0x10
+#define weekday_alarm       0x20
+#define dated_alarm         0x30
+#define timer_alarm_enable  0x40
+#define alarm_int_enable    0x80
+#define timer_alarm_enable  0x40
+#define alarm_int_enable    0x80
+
+// Use the first 2 NVRAM addresses for the century and year bytes.
+#define CENTURY_REG         0x10
+#define YEAR_REG            0x11
+#define USER_REG            0x12
+
+#define TIME                1
+#define ALARM               9
+
+struct Time_t {
+  bool fmt_hours;
+  bool am_pm_flag;
+  char hours;
+  char minutes;
+  char seconds;
+  char hundreds;
+};
+
+struct Date_t {
+  char day;
+  char month;
+  char year;
+  char century;
+  char weekday;
+};
+
+struct DateTime_t {
+  struct Date_t date;
+  struct Time_t time;
+};
+
+class PCF8583rtc {
+  I2C *_i2c;
+  
+public:
+
+/**
+ * Set these public variables according to your locale, default is Australian English.  Default in brackets.
+ *  char *ShortDateFormat;  ("d,m,yy")
+ *  char *LongDateFormat;   ("dddd dd mmmm yyyy")
+ *  char *ShortTimeFormat;  ("d:m:yy")
+ *  char *LongTimeFormat;   ("dd:nn:yyyy")
+ *
+ *  char DateSeparator;     ("\")
+ *  char TimeSeparator;     (":")
+ *
+ *  char *ShortDayNames[7];    ({"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"})
+ *  char *LongDayNames[7];     ({"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"})
+ *  char *ShortMonthNames[12]; ({"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"})
+ *  char *LongMonthNames[12];  ({"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"})
+ *
+* Example:
+* @code
+* #include <mbed.h>
+* #include "PCF8583_rtc.h"
+*
+* I2C i2c(P0_10, P0_11);       // sda, scl
+* PCF8583rtc rtc(&i2c, PCF8583_addr_2);
+* 
+*    struct DateTime_t dtl;
+*
+*    //Set the time
+*    dtl = rtc.read(TIME);
+*    dtl.time.hours = rtc.bin2bcd(11);
+*    dtl.time.minutes = rtc.bin2bcd(43);
+*    rtc.write(TIME, dtl);
+*
+*    //Read and display the time on the nixie display
+*    dtl = rtc.read(TIME);
+*    i2c.write(ADDR_8574_1, &dtl.time.hours, 1);
+*    i2c.write(ADDR_8574_2, &dtl.time.minutes, 1);
+*
+* @endcode
+
+  PCF8583rtc(I2C *i2c, char I2cAddress);
+
+  DateTime_t read(const char address);
+  void   write(const char address, DateTime_t dti);
+  void   FormatDateTime(char *dest, char *format);
+  bool   WriteNVram(char address, char * value, char num);
+  bool   ReadNVram(char address, char * dest, char num);
+  void   SetDateTime(DateTime_t dti);
+  struct DateTime_t GetDateTimeBCD(void);
+
+*/
+
+  char *ShortDateFormat;
+  char *LongDateFormat;
+  char *ShortTimeFormat;
+  char *LongTimeFormat;
+
+  char DateSeparator;
+  char TimeSeparator;
+
+  char *ShortDayNames[7];
+  char *LongDayNames[7];
+  char *ShortMonthNames[12];
+  char *LongMonthNames[12];
+    
+  PCF8583rtc(I2C *i2c, char I2cAddress);
+
+/** read the current Time or Alarm
+*
+* @param address "TIME" = read the time, "ALARM" = read the alarm
+* @returns
+*   a DateTime_t structure
+*/
+  DateTime_t read(const char address);
+
+/** write the current Time or Alarm
+*
+* @param address "TIME" = read the time, "ALARM" = read the alarm 
+* @param dti a DateTime_t structure containing the time or alarm data 
+*/
+  void   write(const char address, DateTime_t dti);
+
+  void   FormatDateTime(char *dest, char *format);
+
+  bool   WriteNVram(char address, char * value, char num);
+
+  bool   ReadNVram(char address, char * dest, char num);
+
+  void   SetDateTime(DateTime_t dti);
+
+  char bin2bcd(char value);
+  
+private:
+  struct DateTime_t dt;
+  char   _I2cAddress;
+
+  char Bcd2Char(char *d, char val, char WantLeadZero);
+  void enableCounting(void);
+  void pauseCounting(void);
+  char readByte(char address);
+  void writeByte(char address, char d);
+  void configureAlarmReg(char alarm);
+  void configureControlReg(char control);
+};
+
+/** PCF8583_rtc class.
+ *
+ * Example:
+ * @code
+ * #include <mbed.h>
+ * #include <PCF8583_rtc.h>
+ *
+ * I2C i2c(P0_10, P0_11);       // sda, scl
+ * PCF8583rtc rtc(&i2c);
+ *
+ * int main() { 
+ *  
+ * rtc.read(TIME);
+ * i2c.write(ADDR_8574_1, &rtc.HoursBCD, 1);   //write hours to display
+ * i2c.write(ADDR_8574_2, &rtc.MinsBCD, 1);    //write minutes to display
+ * }
+ * @endcode
+ */
+ 
+/** Create a PCF8583rtc object using a pointer to the given I2C object.
+ *
+ * @param i2c pointer to an I2C object to which the PCF8583 device is connected.
+ */
+//PCF8583rtc(I2C *i2c);
+
+/** the Read_DateTime function.
+ *
+ * @param address  Determines whether to read the date/time or alarm registers
+ *                 Values are retrieved from the internal datetime structure
+ */
+//void read(const char address);
+
+/** the Write_DateTime function.
+ *
+ * @param address  Determines whether to set the date/time or alarm registers
+ *                 Values are first set into the internal datetime structure prior to calling
+ */
+//void write(const char address);
+ 
+/** the FormatDateTime function.
+ *
+ * @param dest   a pointer to a char array where formatted values are to be placed
+ * @param format a pointer to a string describing how to format the values 
+ */
+//void FormatDateTime(char *dest, char *format);
+
+/** the WriteNVram function.
+ *
+ * @param address  address where value is to be placed
+ * @param value    pointer to char array containing the value(s) to be stored
+ * @param num      the number bytes to store
+ */
+//void WriteNVram(char address, char *value, char num);
+
+//#endif
\ No newline at end of file