ArduinoLibs
DS3232RTC.cpp
00001 /*
00002  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00020  * DEALINGS IN THE SOFTWARE.
00021  */
00022 
00023 #include "DS3232RTC.h"
00024 #include "../I2C/I2CMaster.h"
00025 #if defined(ARDUINO) && ARDUINO >= 100
00026 #include <Arduino.h>
00027 #else
00028 #include <WProgram.h>
00029 #endif
00030 
00059 // I2C address of the RTC chip (7-bit).
00060 #define DS3232_I2C_ADDRESS  0x68
00061 
00062 // Registers.
00063 #define DS3232_SECOND       0x00
00064 #define DS3232_MINUTE       0x01
00065 #define DS3232_HOUR         0x02
00066 #define DS3232_DAY_OF_WEEK  0x03
00067 #define DS3232_DATE         0x04
00068 #define DS3232_MONTH        0x05
00069 #define DS3232_YEAR         0x06
00070 #define DS3232_ALARM1_SEC   0x07
00071 #define DS3232_ALARM1_MIN   0x08
00072 #define DS3232_ALARM1_HOUR  0x09
00073 #define DS3232_ALARM1_DAY   0x0A
00074 #define DS3232_ALARM2_MIN   0x0B
00075 #define DS3232_ALARM2_HOUR  0x0C
00076 #define DS3232_ALARM2_DAY   0x0D
00077 #define DS3232_CONTROL      0x0E
00078 #define DS3232_STATUS       0x0F
00079 #define DS3232_AGING_OFFSET 0x10
00080 #define DS3232_TEMP_MSB     0x11
00081 #define DS3232_TEMP_LSB     0x12
00082 #define DS3232_RESERVED     0x13
00083 #define DS3232_NVRAM        0x14
00084 
00085 // Bits in the DS3232_CONTROL register.
00086 #define DS3232_EOSC         0x80
00087 #define DS3232_BBSQW        0x40
00088 #define DS3232_CONV         0x20
00089 #define DS3232_RS_1HZ       0x00
00090 #define DS3232_RS_1024HZ    0x08
00091 #define DS3232_RS_4096HZ    0x10
00092 #define DS3232_RS_8192HZ    0x18
00093 #define DS3232_INTCN        0x04
00094 #define DS3232_A2IE         0x02
00095 #define DS3232_A1IE         0x01
00096 
00097 // Bits in the DS3232_STATUS register.
00098 #define DS3232_OSF          0x80
00099 #define DS3232_BB32KHZ      0x40
00100 #define DS3232_CRATE_64     0x00
00101 #define DS3232_CRATE_128    0x10
00102 #define DS3232_CRATE_256    0x20
00103 #define DS3232_CRATE_512    0x30
00104 #define DS3232_EN32KHZ      0x08
00105 #define DS3232_BSY          0x04
00106 #define DS3232_A2F          0x02
00107 #define DS3232_A1F          0x01
00108 
00109 // Alarm storage at the end of the RTC's NVRAM.
00110 #define DS3232_ALARM_SIZE   3
00111 #define DS3232_ALARMS       (256 - RTC::ALARM_COUNT * DS3232_ALARM_SIZE - 1)
00112 #define DS3232_ALARM_MAGIC  255
00113 
00126 DS3232RTC::DS3232RTC(I2CMaster &bus, uint8_t oneHzPin)
00127     : _bus(&bus)
00128     , _oneHzPin(oneHzPin)
00129     , prevOneHz(false)
00130     , _isRealTime(true)
00131     , alarmInterrupts(false)
00132 {
00133     // Probe the device and configure it for our use.
00134     _bus->startWrite(DS3232_I2C_ADDRESS);
00135     _bus->write(DS3232_CONTROL);
00136     if (_bus->startRead(DS3232_I2C_ADDRESS, 1)) {
00137         uint8_t value = _bus->read() & DS3232_CONV;
00138         if (oneHzPin != 255)
00139             value |= DS3232_BBSQW | DS3232_RS_1HZ;
00140         _bus->startWrite(DS3232_I2C_ADDRESS);
00141         _bus->write(DS3232_CONTROL);
00142         _bus->write(value);
00143         _bus->write(DS3232_CRATE_64);
00144         _bus->endWrite();
00145     } else {
00146         // Did not get an acknowledgement from the RTC chip.
00147         _isRealTime = false;
00148     }
00149 
00150     // Configure the 1 Hz square wave pin if required.
00151     if (oneHzPin != 255 && _isRealTime) {
00152         pinMode(oneHzPin, INPUT);
00153         digitalWrite(oneHzPin, HIGH);
00154     }
00155 
00156     // Initialize the alarms in the RTC chip's NVRAM.
00157     if (_isRealTime)
00158         initAlarms();
00159 }
00160 
00166 bool DS3232RTC::hasUpdates()
00167 {
00168     // If not using a 1 Hz pin or there is no RTC chip available,
00169     // then assume that there is an update available.
00170     if (_oneHzPin == 255 || !_isRealTime)
00171         return true;
00172 
00173     // The DS3232 updates the internal registers on the falling edge of the
00174     // 1 Hz clock.  The values should be ready to read on the rising edge.
00175     bool value = digitalRead(_oneHzPin);
00176     if (value && !prevOneHz) {
00177         prevOneHz = value;
00178         return true;
00179     } else {
00180         prevOneHz = value;
00181         return false;
00182     }
00183 }
00184 
00185 inline uint8_t fromBCD(uint8_t value)
00186 {
00187     return (value >> 4) * 10 + (value & 0x0F);
00188 }
00189 
00190 inline uint8_t fromHourBCD(uint8_t value)
00191 {
00192     if ((value & 0x40) != 0) {
00193         // 12-hour mode.
00194         uint8_t result = ((value >> 4) & 0x01) * 10 + (value & 0x0F);
00195         if ((value & 0x20) != 0)
00196             return (result == 12) ? 12 : (result + 12);     // PM
00197         else
00198             return (result == 12) ? 0 : result;             // AM
00199     } else {
00200         // 24-hour mode.
00201         return fromBCD(value);
00202     }
00203 }
00204 
00205 void DS3232RTC::readTime(RTCTime *value)
00206 {
00207     if (_isRealTime) {
00208         _bus->startWrite(DS3232_I2C_ADDRESS);
00209         _bus->write(DS3232_SECOND);
00210         if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
00211             value->second = fromBCD(_bus->read());
00212             value->minute = fromBCD(_bus->read());
00213             value->hour = fromHourBCD(_bus->read());
00214         } else {
00215             // RTC chip is not responding.
00216             value->second = 0;
00217             value->minute = 0;
00218             value->hour = 0;
00219         }
00220     } else {
00221         RTC::readTime(value);
00222     }
00223 }
00224 
00225 void DS3232RTC::readDate(RTCDate *value)
00226 {
00227     if (!_isRealTime) {
00228         RTC::readDate(value);
00229         return;
00230     }
00231     _bus->startWrite(DS3232_I2C_ADDRESS);
00232     _bus->write(DS3232_DATE);
00233     if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
00234         value->day = fromBCD(_bus->read());
00235         value->month = fromBCD(_bus->read() & 0x7F); // Strip century bit.
00236         value->year = fromBCD(_bus->read()) + 2000;
00237     } else {
00238         // RTC chip is not responding.
00239         value->day = 1;
00240         value->month = 1;
00241         value->year = 2000;
00242     }
00243 }
00244 
00245 inline uint8_t toBCD(uint8_t value)
00246 {
00247     return ((value / 10) << 4) + (value % 10);
00248 }
00249 
00250 void DS3232RTC::writeTime(const RTCTime *value)
00251 {
00252     if (_isRealTime) {
00253         _bus->startWrite(DS3232_I2C_ADDRESS);
00254         _bus->write(DS3232_SECOND);
00255         _bus->write(toBCD(value->second));
00256         _bus->write(toBCD(value->minute));
00257         _bus->write(toBCD(value->hour));    // Changes mode to 24-hour clock.
00258         _bus->endWrite();
00259     } else {
00260         RTC::writeTime(value);
00261     }
00262 }
00263 
00264 void DS3232RTC::writeDate(const RTCDate *value)
00265 {
00266     if (_isRealTime) {
00267         _bus->startWrite(DS3232_I2C_ADDRESS);
00268         _bus->write(DS3232_DATE);
00269         _bus->write(toBCD(value->day));
00270         _bus->write(toBCD(value->month));
00271         _bus->write(toBCD(value->year % 100));
00272         _bus->endWrite();
00273     } else {
00274         RTC::writeDate(value);
00275     }
00276 }
00277 
00278 void DS3232RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value)
00279 {
00280     if (_isRealTime) {
00281         _bus->startWrite(DS3232_I2C_ADDRESS);
00282         _bus->write(DS3232_ALARMS + alarmNum * DS3232_ALARM_SIZE);
00283         if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
00284             value->hour = fromBCD(_bus->read());
00285             value->minute = fromBCD(_bus->read());
00286             value->flags = _bus->read();
00287         } else {
00288             // RTC chip is not responding.
00289             value->hour = 0;
00290             value->minute = 0;
00291             value->flags = 0;
00292         }
00293     } else {
00294         RTC::readAlarm(alarmNum, value);
00295     }
00296 }
00297 
00298 void DS3232RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
00299 {
00300     if (_isRealTime) {
00301         // Write the alarm details to NVRAM.
00302         _bus->startWrite(DS3232_I2C_ADDRESS);
00303         _bus->write(DS3232_ALARMS + alarmNum * DS3232_ALARM_SIZE);
00304         _bus->write(toBCD(value->hour));
00305         _bus->write(toBCD(value->minute));
00306         _bus->write(value->flags);
00307         _bus->endWrite();
00308 
00309         // Keep the DS3232's built-in alarms in sync with the first two alarms.
00310         if (alarmNum == 0) {
00311             _bus->startWrite(DS3232_I2C_ADDRESS);
00312             _bus->write(DS3232_ALARM1_SEC);
00313             _bus->write(0);
00314             _bus->write(toBCD(value->minute));
00315             _bus->write(toBCD(value->hour));
00316             _bus->write(0x81);  // Match hours, mins, secs; day = 1
00317             _bus->endWrite();
00318             if (alarmInterrupts)
00319                 updateAlarmInterrupts();
00320         } else if (alarmNum == 1) {
00321             _bus->startWrite(DS3232_I2C_ADDRESS);
00322             _bus->write(DS3232_ALARM2_MIN);
00323             _bus->write(toBCD(value->minute));
00324             _bus->write(toBCD(value->hour));
00325             _bus->write(0x81);  // Match hours, mins; day = 1
00326             _bus->endWrite();
00327             if (alarmInterrupts)
00328                 updateAlarmInterrupts();
00329         }
00330     } else {
00331         RTC::writeAlarm(alarmNum, value);
00332     }
00333 }
00334 
00335 int DS3232RTC::byteCount() const
00336 {
00337     return DS3232_ALARMS - DS3232_NVRAM;
00338 }
00339 
00340 uint8_t DS3232RTC::readByte(uint8_t offset)
00341 {
00342     if (_isRealTime)
00343         return readRegister(DS3232_NVRAM + offset);
00344     else
00345         return RTC::readByte(offset);
00346 }
00347 
00348 void DS3232RTC::writeByte(uint8_t offset, uint8_t value)
00349 {
00350     if (_isRealTime)
00351         writeRegister(DS3232_NVRAM + offset, value);
00352     else
00353         RTC::writeByte(offset, value);
00354 }
00355 
00356 int DS3232RTC::readTemperature()
00357 {
00358     if (_isRealTime) {
00359         return (((int)(signed char)readRegister(DS3232_TEMP_MSB)) << 2) |
00360                (readRegister(DS3232_TEMP_LSB) >> 6);
00361     } else {
00362         return NO_TEMPERATURE;
00363     }
00364 }
00365 
00380 void DS3232RTC::enableAlarmInterrupts()
00381 {
00382     if (_oneHzPin == 255 && _isRealTime) {
00383         updateAlarmInterrupts();
00384         alarmInterrupts = true;
00385     }
00386 }
00387 
00393 void DS3232RTC::disableAlarmInterrupts()
00394 {
00395     if (alarmInterrupts) {
00396         uint8_t value = readRegister(DS3232_CONTROL);
00397         value &= ~(DS3232_INTCN | DS3232_A2IE | DS3232_A1IE);
00398         writeRegister(DS3232_CONTROL, value);
00399         alarmInterrupts = false;
00400     }
00401 }
00402 
00416 int DS3232RTC::firedAlarm()
00417 {
00418     if (!_isRealTime)
00419         return -1;
00420     uint8_t value = readRegister(DS3232_STATUS);
00421     int alarm;
00422     if (value & DS3232_A1F) {
00423         if (value & DS3232_A2F)
00424             alarm = 2;
00425         else
00426             alarm = 0;
00427     } else if (value & DS3232_A2F) {
00428         alarm = 1;
00429     } else {
00430         alarm = -1;
00431     }
00432     if (alarm != -1) {
00433         value &= ~(DS3232_A1F | DS3232_A2F);
00434         writeRegister(DS3232_STATUS, value);
00435     }
00436     return alarm;
00437 }
00438 
00444 void DS3232RTC::enable32kHzOutput()
00445 {
00446     if (_isRealTime) {
00447         uint8_t value = readRegister(DS3232_STATUS);
00448         value |= DS3232_BB32KHZ | DS3232_EN32KHZ;
00449         writeRegister(DS3232_STATUS, value);
00450     }
00451 }
00452 
00458 void DS3232RTC::disable32kHzOutput()
00459 {
00460     if (_isRealTime) {
00461         uint8_t value = readRegister(DS3232_STATUS);
00462         value &= ~(DS3232_BB32KHZ | DS3232_EN32KHZ);
00463         writeRegister(DS3232_STATUS, value);
00464     }
00465 }
00466 
00467 void DS3232RTC::initAlarms()
00468 {
00469     uint8_t value = readRegister(DS3232_ALARM_MAGIC);
00470     if (value != (0xB0 + ALARM_COUNT)) {
00471         // This is the first time we have used this clock chip,
00472         // so initialize all alarms to their default state.
00473         RTCAlarm alarm;
00474         alarm.hour = 6;         // Default to 6am for alarms.
00475         alarm.minute = 0;
00476         alarm.flags = 0;
00477         for (uint8_t index = 0; index < ALARM_COUNT; ++index)
00478             writeAlarm(index, &alarm);
00479         writeRegister(DS3232_ALARM_MAGIC, 0xB0 + ALARM_COUNT);
00480 
00481         // Also clear the rest of NVRAM so that it is in a known state.
00482         // Otherwise we'll have whatever garbage was present at power-on.
00483         _bus->startWrite(DS3232_I2C_ADDRESS);
00484         _bus->write(DS3232_NVRAM);
00485         for (uint8_t index = DS3232_NVRAM; index < DS3232_ALARMS; ++index)
00486             _bus->write(0);
00487         _bus->endWrite();
00488     }
00489 }
00490 
00491 uint8_t DS3232RTC::readRegister(uint8_t reg)
00492 {
00493     _bus->startWrite(DS3232_I2C_ADDRESS);
00494     _bus->write(reg);
00495     if (!_bus->startRead(DS3232_I2C_ADDRESS, 1))
00496         return 0;   // RTC chip is not responding.
00497     return _bus->read();
00498 }
00499 
00500 bool DS3232RTC::writeRegister(uint8_t reg, uint8_t value)
00501 {
00502     _bus->startWrite(DS3232_I2C_ADDRESS);
00503     _bus->write(reg);
00504     _bus->write(value);
00505     return _bus->endWrite();
00506 }
00507 
00508 #define DS3232_ALARM1_FLAGS (DS3232_ALARMS + 2)
00509 #define DS3232_ALARM2_FLAGS (DS3232_ALARMS + DS3232_ALARM_SIZE + 2)
00510 
00511 void DS3232RTC::updateAlarmInterrupts()
00512 {
00513     bool alarm1Enabled = ((readRegister(DS3232_ALARM1_FLAGS) & 0x01) != 0);
00514     bool alarm2Enabled = ((readRegister(DS3232_ALARM2_FLAGS) & 0x01) != 0);
00515     uint8_t value = readRegister(DS3232_CONTROL);
00516     value |= DS3232_INTCN;
00517     if (alarm1Enabled)
00518         value |= DS3232_A1IE;
00519     else
00520         value &= ~DS3232_A1IE;
00521     if (alarm2Enabled)
00522         value |= DS3232_A2IE;
00523     else
00524         value &= ~DS3232_A2IE;
00525     writeRegister(DS3232_CONTROL, value);
00526 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator