a copy of PCF8583_rtc lib with some modification

Fork of PCF8583_rtc by Dennis Smith

PCF8583_rtc.cpp

Committer:
pedro_C
Date:
2016-10-19
Revision:
3:c267167e379e
Parent:
2:3291c1f0b986

File content as of revision 3:c267167e379e:

/*
 ********************************************************************************
 * 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;
   
  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);
    _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);
}