ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
DS3232RTC.cpp
1 /*
2  * Copyright (C) 2012 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "DS3232RTC.h"
24 #include "../I2C/I2CMaster.h"
25 #if defined(ARDUINO) && ARDUINO >= 100
26 #include <Arduino.h>
27 #else
28 #include <WProgram.h>
29 #endif
30 
59 // I2C address of the RTC chip (7-bit).
60 #define DS3232_I2C_ADDRESS 0x68
61 
62 // Registers.
63 #define DS3232_SECOND 0x00
64 #define DS3232_MINUTE 0x01
65 #define DS3232_HOUR 0x02
66 #define DS3232_DAY_OF_WEEK 0x03
67 #define DS3232_DATE 0x04
68 #define DS3232_MONTH 0x05
69 #define DS3232_YEAR 0x06
70 #define DS3232_ALARM1_SEC 0x07
71 #define DS3232_ALARM1_MIN 0x08
72 #define DS3232_ALARM1_HOUR 0x09
73 #define DS3232_ALARM1_DAY 0x0A
74 #define DS3232_ALARM2_MIN 0x0B
75 #define DS3232_ALARM2_HOUR 0x0C
76 #define DS3232_ALARM2_DAY 0x0D
77 #define DS3232_CONTROL 0x0E
78 #define DS3232_STATUS 0x0F
79 #define DS3232_AGING_OFFSET 0x10
80 #define DS3232_TEMP_MSB 0x11
81 #define DS3232_TEMP_LSB 0x12
82 #define DS3232_RESERVED 0x13
83 #define DS3232_NVRAM 0x14
84 
85 // Bits in the DS3232_CONTROL register.
86 #define DS3232_EOSC 0x80
87 #define DS3232_BBSQW 0x40
88 #define DS3232_CONV 0x20
89 #define DS3232_RS_1HZ 0x00
90 #define DS3232_RS_1024HZ 0x08
91 #define DS3232_RS_4096HZ 0x10
92 #define DS3232_RS_8192HZ 0x18
93 #define DS3232_INTCN 0x04
94 #define DS3232_A2IE 0x02
95 #define DS3232_A1IE 0x01
96 
97 // Bits in the DS3232_STATUS register.
98 #define DS3232_OSF 0x80
99 #define DS3232_BB32KHZ 0x40
100 #define DS3232_CRATE_64 0x00
101 #define DS3232_CRATE_128 0x10
102 #define DS3232_CRATE_256 0x20
103 #define DS3232_CRATE_512 0x30
104 #define DS3232_EN32KHZ 0x08
105 #define DS3232_BSY 0x04
106 #define DS3232_A2F 0x02
107 #define DS3232_A1F 0x01
108 
109 // Alarm storage at the end of the RTC's NVRAM.
110 #define DS3232_ALARM_SIZE 3
111 #define DS3232_ALARMS (256 - RTC::ALARM_COUNT * DS3232_ALARM_SIZE - 1)
112 #define DS3232_ALARM_MAGIC 255
113 
126 DS3232RTC::DS3232RTC(I2CMaster &bus, uint8_t oneHzPin)
127  : _bus(&bus)
128  , _oneHzPin(oneHzPin)
129  , prevOneHz(false)
130  , _isRealTime(true)
131  , alarmInterrupts(false)
132 {
133  // Probe the device and configure it for our use.
134  _bus->startWrite(DS3232_I2C_ADDRESS);
135  _bus->write(DS3232_CONTROL);
136  if (_bus->startRead(DS3232_I2C_ADDRESS, 1)) {
137  uint8_t value = _bus->read() & DS3232_CONV;
138  if (oneHzPin != 255)
139  value |= DS3232_BBSQW | DS3232_RS_1HZ;
140  _bus->startWrite(DS3232_I2C_ADDRESS);
141  _bus->write(DS3232_CONTROL);
142  _bus->write(value);
143  _bus->write(DS3232_CRATE_64);
144  _bus->endWrite();
145  } else {
146  // Did not get an acknowledgement from the RTC chip.
147  _isRealTime = false;
148  }
149 
150  // Configure the 1 Hz square wave pin if required.
151  if (oneHzPin != 255 && _isRealTime) {
152  pinMode(oneHzPin, INPUT);
153  digitalWrite(oneHzPin, HIGH);
154  }
155 
156  // Initialize the alarms in the RTC chip's NVRAM.
157  if (_isRealTime)
158  initAlarms();
159 }
160 
167 {
168  // If not using a 1 Hz pin or there is no RTC chip available,
169  // then assume that there is an update available.
170  if (_oneHzPin == 255 || !_isRealTime)
171  return true;
172 
173  // The DS3232 updates the internal registers on the falling edge of the
174  // 1 Hz clock. The values should be ready to read on the rising edge.
175  bool value = digitalRead(_oneHzPin);
176  if (value && !prevOneHz) {
177  prevOneHz = value;
178  return true;
179  } else {
180  prevOneHz = value;
181  return false;
182  }
183 }
184 
185 inline uint8_t fromBCD(uint8_t value)
186 {
187  return (value >> 4) * 10 + (value & 0x0F);
188 }
189 
190 inline uint8_t fromHourBCD(uint8_t value)
191 {
192  if ((value & 0x40) != 0) {
193  // 12-hour mode.
194  uint8_t result = ((value >> 4) & 0x01) * 10 + (value & 0x0F);
195  if ((value & 0x20) != 0)
196  return (result == 12) ? 12 : (result + 12); // PM
197  else
198  return (result == 12) ? 0 : result; // AM
199  } else {
200  // 24-hour mode.
201  return fromBCD(value);
202  }
203 }
204 
206 {
207  if (_isRealTime) {
208  _bus->startWrite(DS3232_I2C_ADDRESS);
209  _bus->write(DS3232_SECOND);
210  if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
211  value->second = fromBCD(_bus->read());
212  value->minute = fromBCD(_bus->read());
213  value->hour = fromHourBCD(_bus->read());
214  } else {
215  // RTC chip is not responding.
216  value->second = 0;
217  value->minute = 0;
218  value->hour = 0;
219  }
220  } else {
221  RTC::readTime(value);
222  }
223 }
224 
226 {
227  if (!_isRealTime) {
228  RTC::readDate(value);
229  return;
230  }
231  _bus->startWrite(DS3232_I2C_ADDRESS);
232  _bus->write(DS3232_DATE);
233  if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
234  value->day = fromBCD(_bus->read());
235  value->month = fromBCD(_bus->read() & 0x7F); // Strip century bit.
236  value->year = fromBCD(_bus->read()) + 2000;
237  } else {
238  // RTC chip is not responding.
239  value->day = 1;
240  value->month = 1;
241  value->year = 2000;
242  }
243 }
244 
245 inline uint8_t toBCD(uint8_t value)
246 {
247  return ((value / 10) << 4) + (value % 10);
248 }
249 
250 void DS3232RTC::writeTime(const RTCTime *value)
251 {
252  if (_isRealTime) {
253  _bus->startWrite(DS3232_I2C_ADDRESS);
254  _bus->write(DS3232_SECOND);
255  _bus->write(toBCD(value->second));
256  _bus->write(toBCD(value->minute));
257  _bus->write(toBCD(value->hour)); // Changes mode to 24-hour clock.
258  _bus->endWrite();
259  } else {
260  RTC::writeTime(value);
261  }
262 }
263 
264 void DS3232RTC::writeDate(const RTCDate *value)
265 {
266  if (_isRealTime) {
267  _bus->startWrite(DS3232_I2C_ADDRESS);
268  _bus->write(DS3232_DATE);
269  _bus->write(toBCD(value->day));
270  _bus->write(toBCD(value->month));
271  _bus->write(toBCD(value->year % 100));
272  _bus->endWrite();
273  } else {
274  RTC::writeDate(value);
275  }
276 }
277 
278 void DS3232RTC::readAlarm(uint8_t alarmNum, RTCAlarm *value)
279 {
280  if (_isRealTime) {
281  _bus->startWrite(DS3232_I2C_ADDRESS);
282  _bus->write(DS3232_ALARMS + alarmNum * DS3232_ALARM_SIZE);
283  if (_bus->startRead(DS3232_I2C_ADDRESS, 3)) {
284  value->hour = fromBCD(_bus->read());
285  value->minute = fromBCD(_bus->read());
286  value->flags = _bus->read();
287  } else {
288  // RTC chip is not responding.
289  value->hour = 0;
290  value->minute = 0;
291  value->flags = 0;
292  }
293  } else {
294  RTC::readAlarm(alarmNum, value);
295  }
296 }
297 
298 void DS3232RTC::writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
299 {
300  if (_isRealTime) {
301  // Write the alarm details to NVRAM.
302  _bus->startWrite(DS3232_I2C_ADDRESS);
303  _bus->write(DS3232_ALARMS + alarmNum * DS3232_ALARM_SIZE);
304  _bus->write(toBCD(value->hour));
305  _bus->write(toBCD(value->minute));
306  _bus->write(value->flags);
307  _bus->endWrite();
308 
309  // Keep the DS3232's built-in alarms in sync with the first two alarms.
310  if (alarmNum == 0) {
311  _bus->startWrite(DS3232_I2C_ADDRESS);
312  _bus->write(DS3232_ALARM1_SEC);
313  _bus->write(0);
314  _bus->write(toBCD(value->minute));
315  _bus->write(toBCD(value->hour));
316  _bus->write(0x81); // Match hours, mins, secs; day = 1
317  _bus->endWrite();
318  if (alarmInterrupts)
319  updateAlarmInterrupts();
320  } else if (alarmNum == 1) {
321  _bus->startWrite(DS3232_I2C_ADDRESS);
322  _bus->write(DS3232_ALARM2_MIN);
323  _bus->write(toBCD(value->minute));
324  _bus->write(toBCD(value->hour));
325  _bus->write(0x81); // Match hours, mins; day = 1
326  _bus->endWrite();
327  if (alarmInterrupts)
328  updateAlarmInterrupts();
329  }
330  } else {
331  RTC::writeAlarm(alarmNum, value);
332  }
333 }
334 
336 {
337  return DS3232_ALARMS - DS3232_NVRAM;
338 }
339 
340 uint8_t DS3232RTC::readByte(uint8_t offset)
341 {
342  if (_isRealTime)
343  return readRegister(DS3232_NVRAM + offset);
344  else
345  return RTC::readByte(offset);
346 }
347 
348 void DS3232RTC::writeByte(uint8_t offset, uint8_t value)
349 {
350  if (_isRealTime)
351  writeRegister(DS3232_NVRAM + offset, value);
352  else
353  RTC::writeByte(offset, value);
354 }
355 
357 {
358  if (_isRealTime) {
359  return (((int)(signed char)readRegister(DS3232_TEMP_MSB)) << 2) |
360  (readRegister(DS3232_TEMP_LSB) >> 6);
361  } else {
362  return NO_TEMPERATURE;
363  }
364 }
365 
381 {
382  if (_oneHzPin == 255 && _isRealTime) {
383  updateAlarmInterrupts();
384  alarmInterrupts = true;
385  }
386 }
387 
394 {
395  if (alarmInterrupts) {
396  uint8_t value = readRegister(DS3232_CONTROL);
397  value &= ~(DS3232_INTCN | DS3232_A2IE | DS3232_A1IE);
398  writeRegister(DS3232_CONTROL, value);
399  alarmInterrupts = false;
400  }
401 }
402 
417 {
418  if (!_isRealTime)
419  return -1;
420  uint8_t value = readRegister(DS3232_STATUS);
421  int alarm;
422  if (value & DS3232_A1F) {
423  if (value & DS3232_A2F)
424  alarm = 2;
425  else
426  alarm = 0;
427  } else if (value & DS3232_A2F) {
428  alarm = 1;
429  } else {
430  alarm = -1;
431  }
432  if (alarm != -1) {
433  value &= ~(DS3232_A1F | DS3232_A2F);
434  writeRegister(DS3232_STATUS, value);
435  }
436  return alarm;
437 }
438 
445 {
446  if (_isRealTime) {
447  uint8_t value = readRegister(DS3232_STATUS);
448  value |= DS3232_BB32KHZ | DS3232_EN32KHZ;
449  writeRegister(DS3232_STATUS, value);
450  }
451 }
452 
459 {
460  if (_isRealTime) {
461  uint8_t value = readRegister(DS3232_STATUS);
462  value &= ~(DS3232_BB32KHZ | DS3232_EN32KHZ);
463  writeRegister(DS3232_STATUS, value);
464  }
465 }
466 
467 void DS3232RTC::initAlarms()
468 {
469  uint8_t value = readRegister(DS3232_ALARM_MAGIC);
470  if (value != (0xB0 + ALARM_COUNT)) {
471  // This is the first time we have used this clock chip,
472  // so initialize all alarms to their default state.
473  RTCAlarm alarm;
474  alarm.hour = 6; // Default to 6am for alarms.
475  alarm.minute = 0;
476  alarm.flags = 0;
477  for (uint8_t index = 0; index < ALARM_COUNT; ++index)
478  writeAlarm(index, &alarm);
479  writeRegister(DS3232_ALARM_MAGIC, 0xB0 + ALARM_COUNT);
480 
481  // Also clear the rest of NVRAM so that it is in a known state.
482  // Otherwise we'll have whatever garbage was present at power-on.
483  _bus->startWrite(DS3232_I2C_ADDRESS);
484  _bus->write(DS3232_NVRAM);
485  for (uint8_t index = DS3232_NVRAM; index < DS3232_ALARMS; ++index)
486  _bus->write(0);
487  _bus->endWrite();
488  }
489 }
490 
491 uint8_t DS3232RTC::readRegister(uint8_t reg)
492 {
493  _bus->startWrite(DS3232_I2C_ADDRESS);
494  _bus->write(reg);
495  if (!_bus->startRead(DS3232_I2C_ADDRESS, 1))
496  return 0; // RTC chip is not responding.
497  return _bus->read();
498 }
499 
500 bool DS3232RTC::writeRegister(uint8_t reg, uint8_t value)
501 {
502  _bus->startWrite(DS3232_I2C_ADDRESS);
503  _bus->write(reg);
504  _bus->write(value);
505  return _bus->endWrite();
506 }
507 
508 #define DS3232_ALARM1_FLAGS (DS3232_ALARMS + 2)
509 #define DS3232_ALARM2_FLAGS (DS3232_ALARMS + DS3232_ALARM_SIZE + 2)
510 
511 void DS3232RTC::updateAlarmInterrupts()
512 {
513  bool alarm1Enabled = ((readRegister(DS3232_ALARM1_FLAGS) & 0x01) != 0);
514  bool alarm2Enabled = ((readRegister(DS3232_ALARM2_FLAGS) & 0x01) != 0);
515  uint8_t value = readRegister(DS3232_CONTROL);
516  value |= DS3232_INTCN;
517  if (alarm1Enabled)
518  value |= DS3232_A1IE;
519  else
520  value &= ~DS3232_A1IE;
521  if (alarm2Enabled)
522  value |= DS3232_A2IE;
523  else
524  value &= ~DS3232_A2IE;
525  writeRegister(DS3232_CONTROL, value);
526 }
uint8_t month
Month of the year (1-12)
Definition: RTC.h:38
virtual void writeTime(const RTCTime *value)
Updates the time in the realtime clock to match value.
Definition: RTC.cpp:179
void enableAlarmInterrupts()
Enables the generation of interrupts for alarms 0 and 1.
Definition: DS3232RTC.cpp:380
void disable32kHzOutput()
Disables the 32 kHz output on the DS3232 chip.
Definition: DS3232RTC.cpp:458
void readTime(RTCTime *value)
Reads the current time from the realtime clock into value.
Definition: DS3232RTC.cpp:205
int readTemperature()
Reads the value of the temperature sensor and returns the temperature in quarters of a degree celcius...
Definition: DS3232RTC.cpp:356
uint8_t minute
Minute within the hour (0-59)
Definition: RTC.h:31
virtual void readAlarm(uint8_t alarmNum, RTCAlarm *value)
Reads the details of the alarm with index alarmNum into value.
Definition: RTC.cpp:209
virtual void readDate(RTCDate *value)
Reads the current date from the realtime clock into value.
Definition: RTC.cpp:169
int byteCount() const
Returns the number of bytes of non-volatile memory that can be used for storage of arbitrary settings...
Definition: DS3232RTC.cpp:335
void readDate(RTCDate *value)
Reads the current date from the realtime clock into value.
Definition: DS3232RTC.cpp:225
void enable32kHzOutput()
Enables the 32 kHz output on the DS3232 chip.
Definition: DS3232RTC.cpp:444
virtual void write(uint8_t value)=0
Writes a single byte value on the I2C bus.
void writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
Updates the details of the alarm with index alarmNum from value.
Definition: DS3232RTC.cpp:298
virtual void writeAlarm(uint8_t alarmNum, const RTCAlarm *value)
Updates the details of the alarm with index alarmNum from value.
Definition: RTC.cpp:224
static const uint8_t ALARM_COUNT
Number of alarms that are supported by RTC::readAlarm() and RTC::writeAlarm().
Definition: RTC.h:77
virtual void writeDate(const RTCDate *value)
Updates the date in the realtime clock to match value.
Definition: RTC.cpp:194
void disableAlarmInterrupts()
Disables the generation of interrupts for alarms 0 and 1.
Definition: DS3232RTC.cpp:393
virtual bool startRead(unsigned int address, unsigned int count)=0
Starts a read operation for count bytes by sending the start condition and the I2C control byte...
uint8_t hour
Hour of the day for the alarm (0-23).
Definition: RTC.h:46
static const int NO_TEMPERATURE
Value that is returned from readTemperature() if the realtime clock chip cannot determine the tempera...
Definition: RTC.h:86
uint8_t flags
Additional flags for the alarm.
Definition: RTC.h:49
Stores date information from a realtime clock chip.
Definition: RTC.h:35
void writeTime(const RTCTime *value)
Updates the time in the realtime clock to match value.
Definition: DS3232RTC.cpp:250
uint8_t readByte(uint8_t offset)
Reads the byte at offset within the realtime clock's non-volatile memory.
Definition: DS3232RTC.cpp:340
int firedAlarm()
Determines which of alarms 0 or 1 have fired since the last call.
Definition: DS3232RTC.cpp:416
DS3232RTC(I2CMaster &bus, uint8_t oneHzPin=255)
Attaches to a realtime clock slave device on bus.
Definition: DS3232RTC.cpp:126
virtual void startWrite(unsigned int address)
Starts a write operation by sending a start condition and the I2C control byte.
virtual bool endWrite()=0
Ends the current write operation.
unsigned int year
Year (4-digit)
Definition: RTC.h:37
virtual void writeByte(uint8_t offset, uint8_t value)
Writes value to offset within the realtime clock's non-volatile memory.
Definition: RTC.cpp:262
uint8_t minute
Minute of the hour for the alarm (0-59).
Definition: RTC.h:47
bool hasUpdates()
Returns true if the realtime clock has updated since the last call to this function.
Definition: DS3232RTC.cpp:166
virtual uint8_t readByte(uint8_t offset)
Reads the byte at offset within the realtime clock's non-volatile memory.
Definition: RTC.cpp:247
Stores time information from a realtime clock chip.
Definition: RTC.h:28
Abstract base class for I2C master implementations.
Definition: I2CMaster.h:28
void writeDate(const RTCDate *value)
Updates the date in the realtime clock to match value.
Definition: DS3232RTC.cpp:264
Stores alarm information from a realtime clock chip.
Definition: RTC.h:42
virtual uint8_t read()=0
Reads a single byte from the I2C bus.
uint8_t hour
Hour of the day (0-23)
Definition: RTC.h:30
uint8_t day
Day of the month (1-31)
Definition: RTC.h:39
void readAlarm(uint8_t alarmNum, RTCAlarm *value)
Reads the details of the alarm with index alarmNum into value.
Definition: DS3232RTC.cpp:278
uint8_t second
Second within the minute (0-59)
Definition: RTC.h:32
void writeByte(uint8_t offset, uint8_t value)
Writes value to offset within the realtime clock's non-volatile memory.
Definition: DS3232RTC.cpp:348
virtual void readTime(RTCTime *value)
Reads the current time from the realtime clock into value.
Definition: RTC.cpp:144