This is a library for managing the mbed real time clock, including functions for setting and getting the rtc in text format, getting and setting the timezone offset, and getting and setting the calibration register, both directly, and by using an adjustment API to automatically set the calibration value.

Dependents:   mbed_escm2000

TimeUtilities.cpp

Committer:
WiredHome
Date:
2011-10-30
Revision:
5:fbbdf57675c3
Parent:
3:524ad47afdc7
Child:
6:a517fee06e2e

File content as of revision 5:fbbdf57675c3:

/// @file TimeUtilities.cpp contains a real time clock interface for the mbed
///
/// APIs for showing the time from the RTC, or from a passed in value.
/// Also supports setting the clock, timezone offsets and calibration
/// of the RTC subsystem.
///
/// @note Copyright &copr; 2011 by Smartware Computing, all rights reserved.
///     Individuals may use this application for evaluation or non-commercial
///     purposes. Within this restriction, changes may be made to this application
///     as long as this copyright notice is retained. The user shall make
///     clear that their work is a derived work, and not the original.
///     Users of this application and sources accept this application "as is" and
///     shall hold harmless Smartware Computing, for any undesired results while
///     using this application - whether real or imagined.
///
/// @author David Smart, Smartware Computing
///
#ifndef WIN32
// embedded build
#include "mbed.h"
#endif

#define VERSION "1.03"

#include "TimeUtilities.h"
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef WIN32
// Fake it out for Win32 development and testing
struct LPC {
    unsigned long CCR;          // Clock Control register
    unsigned long GPREG0;       // General Purpose Register #0 - 32-bit Battery backed
    unsigned long GPREG1;       // General Purpose Register #1 - 32-bit Battery backed
    unsigned long CALIBRATION;  // Calibration Register
};
struct LPC X;
struct LPC * LPC_RTC = &X;
#define set_time(x) (void)x
#endif

static int tzOffsetHr = 0;       ///!< time zone offset hours to print time in local time
static int tzOffsetMin = 0;      ///!< time zone offset minutes to print time in local time
static time_t timeWhenLastSet = 0;


static const char ver[] = VERSION;

static int SignOf(const int i) {
    return (i >= 0) ? 1 : -1;
}


RealTimeClock::RealTimeClock() {
    GetTimeOffsetStore();
    GetTimeLastSetStore();
}


RealTimeClock::~RealTimeClock() {
}

const char * RealTimeClock::GetVersion() {
    return ver;
}

time_t RealTimeClock::GetTimeValue() {
    return time(NULL);
}

time_t RealTimeClock::GetTimeValueWhenSet() {
    return timeWhenLastSet;
}


void RealTimeClock::GetTimeString(char *buf, time_t tValue) {
    GetTimeOffsetStore();       // Load the time zone offset values from the battery ram
    GetTimeString(buf, tValue, tzOffsetHr, tzOffsetMin);
}


void RealTimeClock::GetTimeString(char *buf, int hOffset, int mOffset) {
    GetTimeString(buf, 0, hOffset, mOffset);
}


void RealTimeClock::GetTimeString(char *buf, time_t tValue, int hOffset, int mOffset) {
    time_t ctTime;

    if (tValue == 0)
        tValue = time(NULL);
    ctTime = tValue + hOffset * 3600 + SignOf(hOffset) * mOffset * 60;
    strcpy(buf, ctime(&ctTime));
    buf[strlen(buf)-1] = '\0';   // remove the \n
    sprintf(buf + strlen(buf), " (tzo: %2i:%02i)", hOffset, mOffset);
}


int32_t RealTimeClock::GetTimeCalibration() {
    int32_t calvalue = LPC_RTC->CALIBRATION & 0x3FFFF;

    if (calvalue & 0x20000) {
        calvalue = -(calvalue & 0x1FFFF);
    }    
    return calvalue;
}


void RealTimeClock::SetTimeCalibration(int32_t calibration) {
    if (calibration) {
        if (calibration < 0) {
            calibration = (-calibration & 0x1FFFF) | 0x20000;
        }
        LPC_RTC->CCR = 0x000001; //(LPC_RTC->CCR & 0x0003);   // Clear CCALEN to enable it
    } else {
        LPC_RTC->CCR = 0x000011; //(LPC_RTC->CCR & 0x0003) | 0x0010;   // Set CCALEN to disable it
    }
    LPC_RTC->CALIBRATION = calibration;
}


void RealTimeClock::SetTimeOffset(int offsetHour, int offsetMinute) {
    tzOffsetHr = offsetHour;
    tzOffsetMin = offsetMinute;
    SetTimeOffsetStore();
}


void RealTimeClock::SetTimeOffsetStore() {
    LPC_RTC->GPREG0 = tzOffsetHr * 256 + tzOffsetMin;
}


void RealTimeClock::GetTimeOffsetStore() {
    tzOffsetHr = (int32_t)LPC_RTC->GPREG0 / 256;
    tzOffsetMin = (LPC_RTC->GPREG0 & 0xFF);
}

void RealTimeClock::SetTimeLastSetStore() {
    LPC_RTC->GPREG1 = timeWhenLastSet;
}

void RealTimeClock::GetTimeLastSetStore() {
    timeWhenLastSet = LPC_RTC->GPREG1;
}

// MM/DD/YYYY HH:MM:SS [(+/-hh:mm)]
bool RealTimeClock::SetTime(char * timestring) {
    bool success = false;
    char * p;
    time_t seconds = time(NULL);
    struct tm *t = localtime(&seconds);
    int _oH, _oM;
    
    p = strtok(timestring," /");
    if (p != NULL) {
        t->tm_mon = atoi(p) - 1;
        p = strtok(NULL, " /");
        if (p != NULL) {
            t->tm_mday = atoi(p);
            p = strtok(NULL, " /");
            if (p != NULL) {
                t->tm_year = atoi(p) - 1900;
                p = strtok(NULL, " :");
                if (p != NULL) {
                    t->tm_hour = atoi(p);
                    p = strtok(NULL, " :");
                    if (p != NULL) {
                        t->tm_min = atoi(p);
                        p = strtok(NULL, " (:");
                        if (p != NULL) {
                            t->tm_sec = atoi(p);
                            success = true;         // if we get to here, we're good
                            p = strtok(NULL, " (:");
                            if (p != NULL) {
                                success = false;    // but can't accept a partial tzo
                                _oH = atoi(p);
                                p = strtok(NULL, " (:)");
                                if (p != NULL) {
                                    _oM = atoi(p);
                                    success = true; // but a whole tzo is ok
                                    SetTimeOffset(_oH, _oM);
                                }   
                            }
                            seconds = mktime(t);
                            seconds = seconds - (time_t)(tzOffsetHr * 3600 + tzOffsetMin * 60);
                            set_time(seconds);
                            timeWhenLastSet = seconds;
                            SetTimeLastSetStore();
                        }
                    }
                }
            }
        }
    }
    return success;
}

bool RealTimeClock::AdjustBySeconds(int32_t adjustSeconds) {
    if (timeWhenLastSet != 0) {
        time_t seconds = time(NULL);    // get "now" according to the rtc
        int32_t delta = seconds - timeWhenLastSet;
        int32_t curCal = GetTimeCalibration();
        int32_t calMAX = 131071;
        int32_t secPerDay = 86400;
        float errSecPerDay;
                
        // Make the clock correct
        seconds = seconds + adjustSeconds;
        set_time(seconds);
        
        // Convert the current calibration and the adjustment into
        // the new calibration value
        // assume it is +10sec and it has been 2days, then the adjustment
        // needs to be +5 sec per day, or one adjustment every 1/5th 
        // of a day, or 1 adjustment every 86400/5 counts.
        // delta = now - then (number of elapsed seconds)
        if (adjustSeconds != 0 && delta != 0) {
            int32_t calFactor;
            errSecPerDay = (float)adjustSeconds / ((float)(delta)/secPerDay);
            calFactor = (int32_t)((float)secPerDay/errSecPerDay);
            if (abs(calFactor) < calMAX)
                SetTimeCalibration(calFactor);
        }
        return true;
    } else {
        return false;
    }
}