a copy of PCF8583_rtc lib with some modification
Fork of PCF8583_rtc by
Diff: PCF8583_rtc.cpp
- Revision:
- 0:7b654820260e
- Child:
- 1:eaba89d6e5d8
--- /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); +}