Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of PCF8583_rtc by
Revision 0:7b654820260e, committed 2014-02-14
- 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
