ArduinoLibs
|
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 00370 void DS3232RTC::enableAlarmInterrupts() 00371 { 00372 if (_oneHzPin == 255 && _isRealTime) { 00373 updateAlarmInterrupts(); 00374 alarmInterrupts = true; 00375 } 00376 } 00377 00383 void DS3232RTC::disableAlarmInterrupts() 00384 { 00385 if (alarmInterrupts) { 00386 uint8_t value = readRegister(DS3232_CONTROL); 00387 value &= ~(DS3232_INTCN | DS3232_A2IE | DS3232_A1IE); 00388 writeRegister(DS3232_CONTROL, value); 00389 alarmInterrupts = false; 00390 } 00391 } 00392 00406 int DS3232RTC::firedAlarm() 00407 { 00408 if (!_isRealTime) 00409 return -1; 00410 uint8_t value = readRegister(DS3232_STATUS); 00411 int alarm; 00412 if (value & DS3232_A1F) { 00413 if (value & DS3232_A2F) 00414 alarm = 2; 00415 else 00416 alarm = 0; 00417 } else if (value & DS3232_A2F) { 00418 alarm = 1; 00419 } else { 00420 alarm = -1; 00421 } 00422 if (alarm != -1) { 00423 value &= ~(DS3232_A1F | DS3232_A2F); 00424 writeRegister(DS3232_STATUS, value); 00425 } 00426 return alarm; 00427 } 00428 00434 void DS3232RTC::enable32kHzOutput() 00435 { 00436 if (_isRealTime) { 00437 uint8_t value = readRegister(DS3232_STATUS); 00438 value |= DS3232_BB32KHZ | DS3232_EN32KHZ; 00439 writeRegister(DS3232_STATUS, value); 00440 } 00441 } 00442 00448 void DS3232RTC::disable32kHzOutput() 00449 { 00450 if (_isRealTime) { 00451 uint8_t value = readRegister(DS3232_STATUS); 00452 value &= ~(DS3232_BB32KHZ | DS3232_EN32KHZ); 00453 writeRegister(DS3232_STATUS, value); 00454 } 00455 } 00456 00457 void DS3232RTC::initAlarms() 00458 { 00459 uint8_t value = readRegister(DS3232_ALARM_MAGIC); 00460 if (value != (0xB0 + ALARM_COUNT)) { 00461 // This is the first time we have used this clock chip, 00462 // so initialize all alarms to their default state. 00463 RTCAlarm alarm; 00464 alarm.hour = 6; // Default to 6am for alarms. 00465 alarm.minute = 0; 00466 alarm.flags = 0; 00467 for (uint8_t index = 0; index < ALARM_COUNT; ++index) 00468 writeAlarm(index, &alarm); 00469 writeRegister(DS3232_ALARM_MAGIC, 0xB0 + ALARM_COUNT); 00470 00471 // Also clear the rest of NVRAM so that it is in a known state. 00472 // Otherwise we'll have whatever garbage was present at power-on. 00473 _bus->startWrite(DS3232_I2C_ADDRESS); 00474 _bus->write(DS3232_NVRAM); 00475 for (uint8_t index = DS3232_NVRAM; index < DS3232_ALARMS; ++index) 00476 _bus->write(0); 00477 _bus->endWrite(); 00478 } 00479 } 00480 00481 uint8_t DS3232RTC::readRegister(uint8_t reg) 00482 { 00483 _bus->startWrite(DS3232_I2C_ADDRESS); 00484 _bus->write(reg); 00485 if (!_bus->startRead(DS3232_I2C_ADDRESS, 1)) 00486 return 0; // RTC chip is not responding. 00487 return _bus->read(); 00488 } 00489 00490 bool DS3232RTC::writeRegister(uint8_t reg, uint8_t value) 00491 { 00492 _bus->startWrite(DS3232_I2C_ADDRESS); 00493 _bus->write(reg); 00494 _bus->write(value); 00495 return _bus->endWrite(); 00496 } 00497 00498 #define DS3232_ALARM1_FLAGS (DS3232_ALARMS + 2) 00499 #define DS3232_ALARM2_FLAGS (DS3232_ALARMS + DS3232_ALARM_SIZE + 2) 00500 00501 void DS3232RTC::updateAlarmInterrupts() 00502 { 00503 bool alarm1Enabled = ((readRegister(DS3232_ALARM1_FLAGS) & 0x01) != 0); 00504 bool alarm2Enabled = ((readRegister(DS3232_ALARM2_FLAGS) & 0x01) != 0); 00505 uint8_t value = readRegister(DS3232_CONTROL); 00506 value |= DS3232_INTCN; 00507 if (alarm1Enabled) 00508 value |= DS3232_A1IE; 00509 else 00510 value &= ~DS3232_A1IE; 00511 if (alarm2Enabled) 00512 value |= DS3232_A2IE; 00513 else 00514 value &= ~DS3232_A2IE; 00515 writeRegister(DS3232_CONTROL, value); 00516 }