Simple RTC class based on DS1307. Emphasis on simple. Allows you to run at 100k or 400k Hz (for newer DS1307 capable devices). MapTime() allows you to set the time() service to the same as the RTC. Uses struct tm throughout so you can use traditional time functions for manipulation.

Dependents:   AdaFruit_RGBLCD

RTclock.cpp

Committer:
vtraveller
Date:
2014-08-30
Revision:
9:3a0ba8364ef2
Parent:
7:3621025e7949
Child:
10:e5eabd3a1ca6

File content as of revision 9:3a0ba8364ef2:

#include "mbed.h"
#include "RTclock.h"

RTclock::RTclock(PinName in_nSDA, PinName in_nSCL, EClockType in_eClockType, bool in_bHiSpeed)
    : RTclock_parent(in_nSDA,in_nSCL)
    , m_bTwelveHour(false)
    , m_eClockType(in_eClockType)
{        
    // Frequency depends on chip - most are 100KHz
    frequency(in_bHiSpeed ? 400000 : 100000);
}

RTclock::~RTclock()
{
}

int RTclock::bcdToDecimal(int in_nBCD)
{
    return ((in_nBCD & 0xF0) >> 4) * 10 + (in_nBCD & 0x0F);
}

int RTclock::decimalToBcd(int in_nDecimal)
{
    return (in_nDecimal % 10) + ((in_nDecimal / 10) << 4);
}

bool RTclock::getTime(tm & out_sTM)
{
    char aBuffer[7];
    
    if (!read(0, aBuffer, 7)) return false;

    m_bTwelveHour = ((aBuffer[2] & 0x40) == 0x40);
    
    out_sTM.tm_sec = bcdToDecimal(aBuffer[0] & 0x7f);
    out_sTM.tm_min = bcdToDecimal(aBuffer[1]);
    
    if (m_bTwelveHour)
    {
        // add 12 hours if PM bit is set and past midday
        out_sTM.tm_hour = bcdToDecimal(aBuffer[2] & 31);
        
        bool bPM = (0 != (aBuffer[2] & 0x20));
        if (bPM && 12 != out_sTM.tm_hour) out_sTM.tm_hour += 12;
    }
    else
    {
        out_sTM.tm_hour = bcdToDecimal(aBuffer[2] & 0x3f);
    }
    
    out_sTM.tm_wday = aBuffer[3] % 7;
    out_sTM.tm_mday = bcdToDecimal(aBuffer[4]);
    out_sTM.tm_mon  = bcdToDecimal(aBuffer[5]);
    out_sTM.tm_year = (bcdToDecimal(aBuffer[6]) + 2000) - 1900;   //  Returns from 2000, need form 1900 for time function
    out_sTM.tm_isdst = 0;
    
    return true;
}

bool RTclock::isTwelveHour()
{
    return m_bTwelveHour;
}

bool RTclock::mapTime()
{
    tm sTM;
    if (!getTime(sTM)) return false;
    
    // Convert and set internal time
    time_t nTime = ::mktime(&sTM);
    ::set_time(nTime);
    
    return true;
}

bool RTclock::read(int in_nAddress, char * out_pBuffer, int in_nLength)
{
    char aBuffer[2] = { (char)in_nAddress, 0 };
    
    if (0 != RTclock_parent::write(0xd0, aBuffer, 1)) return false;
    if (0 != RTclock_parent::read(0xd0, out_pBuffer, in_nLength)) return false;
    
    return true;
}

bool RTclock::setTime(const tm  & in_sTM, bool in_bTwelveHour)
{
    char aBuffer[7];

    // Preserve flags that were in register
    if (!read(0,aBuffer,7)) return false;
    
    m_bTwelveHour = in_bTwelveHour;
    
    // We always have tm in 24hr form - so adjut if 12hr clock
    int nHour = in_sTM.tm_hour;
    if (in_bTwelveHour) nHour %= 12;
    
    switch (m_eClockType)
    {
        case eDS1311:
            aBuffer[0] &= 0x7f;
            aBuffer[0] = (aBuffer[0] & 0x80) | (decimalToBcd(in_sTM.tm_sec)& 0x7f);
            aBuffer[1] = decimalToBcd(in_sTM.tm_min);    
            aBuffer[2] = (aBuffer[2] & 0xc4) | (decimalToBcd(nHour) & 0x3f);
            aBuffer[3] = in_sTM.tm_wday;
            aBuffer[4] = decimalToBcd(in_sTM.tm_mday);
            aBuffer[5] = decimalToBcd(in_sTM.tm_mon);
            aBuffer[6] = decimalToBcd(in_sTM.tm_year + 1900 - 2000);
        
            // Handle the 12hr clock bits
            if (in_bTwelveHour)
            {
                // Turn on 12hr clock
                aBuffer[2] |= 0x40;
                
                // Set am/pm bit based on hours
                if (in_sTM.tm_hour >= 12) aBuffer[2] |= 0x20; else aBuffer[2] &= ~0x20;        
            }
            else
            {
                aBuffer[2] &= ~64;
            }
            break;
            
        case eDS3231:
            aBuffer[0] = decimalToBcd(in_sTM.tm_sec) & 0x7f;
            aBuffer[1] = decimalToBcd(in_sTM.tm_min) & 0x7f;    
            aBuffer[2] = decimalToBcd(nHour) & (m_bTwelveHour ? 0x1f : 0x3f);
            aBuffer[3] = in_sTM.tm_wday;
            aBuffer[4] = decimalToBcd(in_sTM.tm_mday);
            aBuffer[5] = decimalToBcd(in_sTM.tm_mon) & 0x80 /* 2000+ */;
            aBuffer[6] = decimalToBcd(in_sTM.tm_year + 1900 - 2000);
        
            // Handle the 12hr clock bits
            if (in_bTwelveHour)
            {
                // Turn on 12hr clock
                aBuffer[2] |= 0x40;
                
                // Set am/pm bit based on hours
                if (in_sTM.tm_hour >= 12) aBuffer[2] |= 0x20;
            }
            break;
    }        
    
    // Write new date and time
    setRunning(false);
    
    bool bSuccess = write(0, aBuffer, 7);
    
    if (bSuccess) setRunning(true);

    return bSuccess;
}

bool RTclock::setRunning(bool in_bEnable)
{
    char nRunning;
    
    if (!read(0, &nRunning, 1)) return false;

    // Set running
    if (in_bEnable)
    { 
        nRunning &= 0x7F;
    }
    else
    {
        nRunning |= 0x80;
    }
    
    return write(0, &nRunning, 1);
}

bool RTclock::setSquareWaveOutput
(
    bool                in_bEnable,
    ESquareWaveRates    in_nRateSelect
)
{
    char nValue;
    
    // Read register    
    if (!read(7, &nValue, 1)) return false;
    
    //  Protect control bits
    nValue = (nValue & 0x80) | (in_bEnable ? 0x10 : 0) | ((char)in_nRateSelect & 0x03);
    
    return write(7, &nValue, 1);
}

bool RTclock::write(int in_nAddress, const char * in_pBuffer, int in_nLength)
{    
    char aBuffer[10];
    
    aBuffer[0] = in_nAddress & 0xff;
    
    for (size_t i = 0 ; i < in_nLength; i++)
        aBuffer[i + 1] = in_pBuffer[i];

    return RTclock_parent::write(0xd0, aBuffer, in_nLength + 1);
}