a copy of PCF8583_rtc lib with some modification
Fork of PCF8583_rtc by
PCF8583_rtc.cpp
- Committer:
- dennyem
- Date:
- 2014-02-23
- Revision:
- 1:eaba89d6e5d8
- Parent:
- 0:7b654820260e
- Child:
- 2:3291c1f0b986
File content as of revision 1:eaba89d6e5d8:
/* ******************************************************************************** * 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 DWS initial design for PIC devices using PICC compiler * 21.01.14 DWS ported to mBed LPC1768 in 'C' * 09.02.14 DWS 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); }