Justin Howard / RTclock

Dependents:   AdaFruit_RGBLCD

RTclock.cpp

Committer:
vtraveller
Date:
2014-10-08
Revision:
15:1645f55bd0ee
Parent:
14:d5b47ff12d17
Child:
16:f7e4b4cbfb9e

File content as of revision 15:1645f55bd0ee:

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

RTclock::RTclock(I2C & in_cI2C, uint8_t in_nAddress, EClockType in_eClockType)
    : m_bTwelveHour(false)
    , m_cI2C(in_cI2C)
    , m_nAddress(in_nAddress)
    , m_eClockType(in_eClockType)
{        
}

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] & 0x1f);
        
        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(uint8_t in_nAddress, char * out_pBuffer, int in_nLength)
{
    if (0 != m_cI2C.write(m_nAddress, (char *)&in_nAddress, 1)) return false;
    if (0 != m_cI2C.read(m_nAddress, 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(uint8_t 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 m_cI2C.write(m_nAddress, aBuffer, in_nLength + 1);
}