Library for Real Time Clock module MCP97410 based on Library for DS1307

Fork of RTC-DS1307 by Henry Leinen

Revision:
10:780027029afe
Parent:
9:5627b407e097
Child:
11:ef48dcb888c9
diff -r 5627b407e097 -r 780027029afe Rtc_Mcp97410.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Rtc_Mcp97410.cpp	Wed Jan 07 21:46:02 2015 +0000
@@ -0,0 +1,274 @@
+#include "mbed.h"
+#include "Rtc_Mcp97410.h"
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+#include "debug.h"
+
+const char *Rtc_Mcp97410::m_weekDays[] = { "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };    
+
+
+Rtc_Mcp97410::Rtc_Mcp97410(I2C* i2c)
+{
+   
+    m_rtc = i2c;
+    if (m_rtc == NULL)
+        error("Rtc_Mcp97410: no I2C specified");
+        
+    // Set the frequency to standard 100kHz
+    // MCP97410 can handle full 400kHz-speed!
+    //m_rtc->frequency(100000);
+}
+
+Rtc_Mcp97410::~Rtc_Mcp97410()
+{
+    
+}
+
+bool Rtc_Mcp97410::setTime(Time_rtc& time, bool start, bool thm)
+{
+    char buffer[7];
+    INFO("reading clock registers to write the new time : %d:%d:%d\n", time.hour, time.min, time.sec);
+    if (!read(0,buffer,7)) {
+        ERR("Failed to read from RTC\n");
+        return false;
+    }
+    //  Now update only the time part (saving the existing flags)
+    if (start) { buffer[0] |= 0x80; } else { buffer[0] &= 0x7F; }
+    buffer[0] = (buffer[0]&0x80) | (decimalToBcd(time.sec)& 0x7f);
+    buffer[1] = decimalToBcd(time.min);
+    if (thm) {
+        //  AM PM format
+        buffer[2] = (buffer[2]& 196) | (time.hour>12 ? (0x20 | ((decimalToBcd(time.hour-12)))) : decimalToBcd(time.hour));
+    }
+    else {
+        // 24 hours format
+        buffer[2] = (buffer[2]& 196) | (decimalToBcd(time.hour) & 0x3F);
+    }
+    // bit 3 of register 03 on MCP97410 is VBATEN (different to DS1307)!
+    // should be set to 1 to enable Battery-Backup
+    buffer[3] = 0x08 | (time.wday & 0x07);
+    buffer[4] = decimalToBcd(time.date);
+    buffer[5] = decimalToBcd(time.mon);
+    buffer[6] = decimalToBcd(time.year-2000);
+    INFO("Writing new time and date data to RTC\n");
+    if (!write(0, buffer, 7) ) {
+        ERR("Failed to write the data to RTC!\n");
+        return false;
+    }
+    return true;
+}
+
+bool Rtc_Mcp97410::getTime(Time_rtc& time)
+{
+    char buffer[7];
+    bool thm = false;
+    
+    INFO("Getting time from RTC\n");
+    if (!read(0, buffer, 7) ) {
+        //  Failed to read
+        ERR("Failed to read from RTC\n");
+        return false;
+    }
+    thm = ((buffer[2] & 64) == 64);
+    time.sec = bcdToDecimal(buffer[0]&0x7F);
+    time.min = bcdToDecimal(buffer[1]);
+    if (thm) {
+        // in 12-hour-mode, we need to add 12 hours if PM bit is set
+        time.hour = Rtc_Mcp97410::bcdToDecimal( buffer[2] & 31 );
+        if ((buffer[2] & 32) == 32)
+            time.hour += 12;
+    }
+    else {
+        time.hour = Rtc_Mcp97410::bcdToDecimal( buffer[2] & 63 );
+    }  
+    time.wday = buffer[3] & 0x07;
+    INFO("OSCRUN:%0d PWRFAIL:%0d VBATEN:%0d\n", (buffer[3]>>5) & 0x01, buffer[3]>>4 & 0x01, buffer[3]>>3 & 0x01);
+    time.date = Rtc_Mcp97410::bcdToDecimal( buffer[4]);
+    time.mon = Rtc_Mcp97410::bcdToDecimal( buffer[5]);
+    time.year = Rtc_Mcp97410::bcdToDecimal(buffer[6]) + 2000;   //  plus hundret is because RTC is giving the years since 2000, but std c struct tm needs years since 1900
+    
+    return true;
+}
+
+
+bool Rtc_Mcp97410::startClock()
+{
+    char strtStop;
+    
+    INFO ("Reading clock start/stop register value\n");
+    if (!read(0, &strtStop, 1)) {
+        ERR("Failed to read clock start stop register !\n");
+        return false;
+    }
+    
+    strtStop |= 0x80;
+    
+    
+    INFO("Writing back start/stop register value\n");
+    if (!write(0, &strtStop, 1)) {
+        ERR("Failed to write the start stop register !\n");
+        return false;
+    }
+    
+    INFO("Start/stop register value successfully written\n");
+    return true;
+}
+
+bool Rtc_Mcp97410::stopClock()
+{
+    char strtStop;
+    
+    INFO ("Reading clock start/stop register value\n");
+    if (!read(0, &strtStop, 1)) {
+        ERR("Failed to read clock start stop register !\n");
+        return false;
+    }
+    
+    strtStop &= 0x7F;
+    
+    INFO("Writing back start/stop register value\n");
+    if (!write(0, &strtStop, 1)) {
+        ERR("Failed to write the start stop register !\n");
+        return false;
+    }
+    
+    INFO("Start/stop register value successfully written\n");
+    return true;
+}
+
+bool Rtc_Mcp97410::setSquareWaveOutput(bool ena, SqwRateSelect_t rs)
+{
+    char reg;
+    INFO("Reading register value first\n");
+    
+    if (!read(7,&reg, 1)) {
+        ERR("Failed to read register value !\n");
+        return false;
+    }
+    INFO("[Reg:0x07] = %02x\n", reg); 
+    
+    //  preserve the OUT control bit while writing the frequency and enable bits
+    reg = (reg & 0x80) | (ena ? 0x10 : 0) | ((char)rs & 0x03);
+
+    INFO("Writing back register value\n");
+    INFO("[Reg:0x07] = %02x\n", reg); 
+    
+    if (!write(7, &reg, 1)) {
+        ERR("Failed to write register value !\n");
+        return false;
+    }
+    
+    INFO("Successfully changed the square wave output.\n");
+    return true;
+}
+
+
+
+bool Rtc_Mcp97410::read(int address, char *buffer, int len)
+{
+    char buffer2[2] = {(char)address, 0};
+    
+//    m_rtc->start();
+    if (m_rtc->write(0xDF, buffer2, 1) != 0) {
+        ERR("Failed to write register address on rtv!\n");
+        m_rtc->stop();
+        return false;
+    }
+    if (m_rtc->read(0xDF, buffer, len) != 0) {
+        ERR("Failed to read register !\n");
+        return false;
+    }
+    m_rtc->stop();
+    
+    INFO("Successfully read %d registers from RTC\n", len);
+    return true;
+}
+
+bool Rtc_Mcp97410::write(int address, char *buffer, int len)
+{
+    char buffer2[10];
+    buffer2[0] = address&0xFF;
+    for (int i = 0 ; i < len ; i++)
+        buffer2[i+1] = buffer[i];
+
+//    m_rtc->start();        
+    if (m_rtc->write(0xDF, buffer2, len+1) != 0) {
+        ERR("Failed to write data to rtc\n");
+        m_rtc->stop();
+        return false;
+    }
+    m_rtc->stop();
+    return true;
+}
+
+
+
+
+RtcCls::RtcCls(I2C* i2c, PinName sqw, bool bUseSqw)
+    : Rtc_Mcp97410(i2c), m_sqw(sqw), m_bUseSqw(bUseSqw), m_bAlarmEnabled(false), m_alarmfunc(NULL)
+{
+    Time_rtc t;
+    //  query time from device
+    getTime(t);
+    //  sync the time with MBED RTC
+    struct tm now = {t.sec, t.min, t.hour, t.date, t.mon-1, t.year-1900};
+    m_time = mktime(&now);
+    set_time(m_time);
+    
+    //  Only register the callback and start the SQW if requested to do so. Otherwise the system
+    //  will use the MBED built-in RTC.
+    if (m_bUseSqw) {
+        //  start the wave
+        setSquareWaveOutput(true, RS1Hz);
+        //  register callback from now on the time will be maintained by the square wave input
+        m_sqw.rise(this, &RtcCls::_callback);
+    }
+}
+
+void RtcCls::_callback(void)
+{
+//    INFO("Tick!");
+    //  Simply increase the number of seconds
+    m_time++;
+//    if (m_bAlarmEnabled && (m_time == m_alarmTime)) {
+//        if (m_alarmfunc != NULL)
+//            m_alarmfunc();
+//        m_bAlarmEnabled = false;
+//    }
+}
+
+time_t RtcCls::getTime()
+{
+    //  when not using the HW support, we have to query the RTC chip. Other wise we can just return out stored value
+    if (!m_bUseSqw) {
+        Time_rtc t;
+        getTime(t);
+        struct tm now = {t.sec, t.min, t.hour, t.date, t.mon-1, t.year-1900};
+        m_time = mktime(&now);
+        INFO("getting time %02d.%02d.%04d %02d:%02d:%02d Ticks=%08lx", t.date, t.mon, t.year, t.hour, t.min, t.sec, m_time);
+    }
+    else {
+        INFO("getting time Ticks=%08lx", m_time);
+    }
+    return m_time;
+}
+
+void RtcCls::setTime(time_t t)
+{
+    Time_rtc tim;
+    struct tm *now;
+    now = localtime(&t);
+
+    tim.sec = now->tm_sec;
+    tim.min = now->tm_min;
+    tim.hour = now->tm_hour;
+    tim.date = now->tm_mday;
+    tim.mon = now->tm_mon+1;
+    tim.year = now->tm_year + 1900;
+    tim.wday = now->tm_wday +1;
+
+    setTime( tim, true, true);
+    set_time(t);
+}