A time interface class. This class replicates the normal time functions, but goes a couple of steps further. mbed library 82 and prior has a defective gmtime function. Also, this class enables access to setting the time, and adjusting the accuracy of the RTC.

Dependencies:   CalendarPage

Dependents:   CI-data-logger-server WattEye X10Svr SSDP_Server

TimeInterface.cpp

Committer:
WiredHome
Date:
2015-08-06
Revision:
5:a5f50b5fb856
Parent:
3:49f36b489b64
Child:
6:c79cfe750416

File content as of revision 5:a5f50b5fb856:


#include "TimeInterface.h"

#include "rtc_api.h"

//#define DEBUG "Time"
#include <cstdio>
#if (defined(DEBUG) && !defined(TARGET_LPC11U24))
#define DBG(x, ...)  std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define ERR(x, ...)  std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#else
#define DBG(x, ...)
#define WARN(x, ...)
#define ERR(x, ...)
#define INFO(x, ...)
#endif

#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


TimeInterface::TimeInterface()
{
}

TimeInterface::~TimeInterface()
{
}

NTPResult TimeInterface::setTime(const char* host, uint16_t port, uint32_t timeout)
{
    NTPClient ntp;
    NTPResult res;
    // int16_t tzomin = get_tzo_min();
    INFO("setTime(%s, %d, %d) %d\r\n", host, port, timeout, tzomin);
    res = ntp.setTime(host, port, timeout);
    INFO("  ret: %d\r\n", res);
    if (res == NTP_OK) {
        // if the time was fetched successfully, then 
        // let's save the time last set with the local tzo applied
        // and this saves the last time set for later precision
        // tuning.
        set_time(std::time(NULL));
    }
    return res;
}

void set_dst(bool dst)
{
    (void)dst;
}

bool get_dst(void)
{
    return false;   
}

clock_t TimeInterface::clock(void)
{
    return std::clock();
}

time_t TimeInterface::time(time_t* timer)
{
    return std::time(timer);
}

time_t TimeInterface::timelocal(time_t* timer)
{
    return std::time(timer) + 60 * get_tzo_min();
}

char * TimeInterface::ctime(const time_t * timer)
{
    char * p = std::ctime(timer);
    
    if (strlen(p) < sizeof(result)) {
        strcpy(result, p);
        p = strchr(result, '\n');
        if (p)
            *p = '\0';
    } else {
        result[0] = '\0';
    }
    return result;
}

char * TimeInterface::asctime(const struct tm_ex * timeptr)
{
    static const char wday_name[][4] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    };
    static const char mon_name[][4] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    
    sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d",
            wday_name[timeptr->tm_wday % 7],
            mon_name[timeptr->tm_mon % 12],
            timeptr->tm_mday, timeptr->tm_hour,
            timeptr->tm_min, timeptr->tm_sec,
            1900 + timeptr->tm_year);
    return result;
}

struct tm_ex * TimeInterface::gmtime(const time_t * timer)
{
    time_t priv = *timer + (get_tzo_min() * 60);
    struct tm * tmp = std::localtime(&priv);
    
    tm_ext.tm_sec     = tmp->tm_sec;
    tm_ext.tm_min     = tmp->tm_min;
    tm_ext.tm_hour    = tmp->tm_hour;
    tm_ext.tm_mday    = tmp->tm_mday;
    tm_ext.tm_mon     = tmp->tm_mon;
    tm_ext.tm_year    = tmp->tm_year;
    tm_ext.tm_wday    = tmp->tm_wday;
    tm_ext.tm_yday    = tmp->tm_yday;
    tm_ext.tm_isdst   = tmp->tm_isdst;
    tm_ext.tm_tzo_min = get_tzo_min();
    return &tm_ext;
}

struct tm_ex * TimeInterface::localtime(const time_t * timer)
{
    struct tm * tmp = std::localtime(timer);
    
    tm_ext.tm_sec = tmp->tm_sec;
    tm_ext.tm_min = tmp->tm_min;
    tm_ext.tm_hour = tmp->tm_hour;
    tm_ext.tm_mday = tmp->tm_mday;
    tm_ext.tm_mon = tmp->tm_mon;
    tm_ext.tm_year = tmp->tm_year;
    tm_ext.tm_wday = tmp->tm_wday;
    tm_ext.tm_yday = tmp->tm_yday;
    tm_ext.tm_isdst = tmp->tm_isdst;
    tm_ext.tm_tzo_min = get_tzo_min();
    return &tm_ext;
}

time_t TimeInterface::mktime(struct tm_ex * timeptr)
{
    return std::mktime((struct tm *)timeptr);
}

size_t TimeInterface::strftime(char * ptr, size_t maxsize, const char * format, const struct tm_ex * timeptr)
{
    return std::strftime(ptr, maxsize, format, (struct tm *)timeptr);
}

double TimeInterface::difftime(time_t end, time_t beginning)
{
    return std::difftime(end, beginning);
}



// time zone functions

void TimeInterface::set_time(time_t t, int16_t tzo_min)
{
    time_t tval = t - (tzo_min * 60);
    rtc_init();
    rtc_write(tval);
    LPC_RTC->GPREG1 = tval;
    INFO("set_time(%s)", ctime(&tval));
}

void TimeInterface::set_tzo_min(int16_t tzo_min)
{
    uint16_t th;
    uint32_t treg;
    
    if (tzo_min >= -720 && tzo_min <= 720) {
        th = (uint16_t)(-tzo_min);
        treg = (th << 16) | (uint16_t)tzo_min;
        LPC_RTC->GPREG0 = treg;
        //printf("set_tzo(%d) %d is %08X\r\n", tzo, th, LPC_RTC->GPREG0);
    }
}

int16_t TimeInterface::get_tzo_min(void)
{
    uint16_t th, tl;
    
    th = LPC_RTC->GPREG0 >> 16;
    tl = LPC_RTC->GPREG0;
    //printf("get_tzo() is %04X %04X\r\n", th, tl);
    if ((uint16_t)(th + tl) == 0) {
        return tl;
    } else {
        return 0;
    }
}

time_t TimeInterface::get_timelastset(void)
{
    return LPC_RTC->GPREG1;
}

int32_t TimeInterface::get_cal() {
    int32_t calvalue = LPC_RTC->CALIBRATION & 0x3FFFF;

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

void TimeInterface::set_cal(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;
}

bool TimeInterface::adjust_sec(int32_t adjustSeconds)
{
    time_t lastSet = get_timelastset();
    
    if (lastSet != 0) {
        time_t seconds = time(NULL);    // get "now" according to the rtc
        int32_t delta = seconds - lastSet;
        //int32_t curCal = get_cal();   // calibration might want to leverage the current cal factor.
        int32_t calMAX = 131071;
        int32_t secPerDay = 86400;
        float errSecPerDay;
                
        // 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;

            // Make the clock correct
            seconds = seconds + adjustSeconds;
            set_time(seconds);
            // Compute the calibration factor
            errSecPerDay = (float)adjustSeconds / ((float)(delta)/secPerDay);
            calFactor = (int32_t)((float)secPerDay/errSecPerDay);
            if (abs(calFactor) < calMAX)
                set_cal(calFactor);
        }
        return true;
    } else {
        return false;
    }
}