Library for Real Time Clock module MCP97410 based on Library for DS1307
Fork of RTC-DS1307 by
Diff: Rtc_Mcp97410.cpp
- 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,®, 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, ®, 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); +}